<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>RESTful 接口实现与测试( Spring Boot ) | 鱼皮的编程宝典</title>
    <meta name="generator" content="VuePress 1.9.10">
    <link rel="icon" href="/favicon.ico">
    <script>
        var _hmt = _hmt || [];
        (function() {
          var hm = document.createElement("script");
          hm.src = "https://hm.baidu.com/hm.js?2675818a983a3131404cee835018f016";
          var s = document.getElementsByTagName("script")[0]; 
          s.parentNode.insertBefore(hm, s);
        })();
      </script>
    <meta name="description" content="贴心的编程学习路线，全面的编程知识百科">
    <meta property="article:modified_time" content="2023-11-11T12:17:47.000Z">
    <meta property="og:site_name" content="鱼皮的编程宝典">
    <meta property="og:title" content="RESTful 接口实现与测试( Spring Boot )">
    <meta property="og:type" content="article">
    <meta property="og:url" content="https://codefather.cn/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/">
    <meta name="twitter:title" content="RESTful 接口实现与测试( Spring Boot )">
    <meta name="twitter:url" content="https://codefather.cn/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/">
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:label1" content="Written by">
    <meta name="twitter:label2" content="Filed under">
    <meta name="twitter:data2" content="程序员, 编程, 计算机">
    <meta property="article:tag" content="程序员">
    <meta name="keywords" content="程序员鱼皮, 编程学习路线, 编程知识百科, Java, 编程导航, 前端, 开发, 编程分享, 项目, IT, 求职, 面经">
    
    <link rel="preload" href="/assets/css/0.styles.40c15e22.css" as="style"><link rel="preload" href="/assets/js/app.12ab4756.js" as="script"><link rel="preload" href="/assets/js/2.fd06b53e.js" as="script"><link rel="preload" href="/assets/js/62.bbb30ef1.js" as="script"><link rel="prefetch" href="/assets/js/1.17ea1209.js"><link rel="prefetch" href="/assets/js/10.d9532d8f.js"><link rel="prefetch" href="/assets/js/100.064ea126.js"><link rel="prefetch" href="/assets/js/101.d64c5b33.js"><link rel="prefetch" href="/assets/js/102.5e29a62c.js"><link rel="prefetch" href="/assets/js/103.59ad6cd7.js"><link rel="prefetch" href="/assets/js/104.ae4763e8.js"><link rel="prefetch" href="/assets/js/105.2a9124be.js"><link rel="prefetch" href="/assets/js/106.529bca07.js"><link rel="prefetch" href="/assets/js/107.c8671a0d.js"><link rel="prefetch" href="/assets/js/108.55c93c64.js"><link rel="prefetch" href="/assets/js/109.7d1efa54.js"><link rel="prefetch" href="/assets/js/11.2f89b5eb.js"><link rel="prefetch" href="/assets/js/110.0caecbf5.js"><link rel="prefetch" href="/assets/js/111.a2aeae49.js"><link rel="prefetch" href="/assets/js/112.eb7ceda5.js"><link rel="prefetch" href="/assets/js/113.f304408d.js"><link rel="prefetch" href="/assets/js/114.988c637f.js"><link rel="prefetch" href="/assets/js/115.bfdeed31.js"><link rel="prefetch" href="/assets/js/116.a47b8c53.js"><link rel="prefetch" href="/assets/js/117.2e1e8250.js"><link rel="prefetch" href="/assets/js/118.758ce408.js"><link rel="prefetch" href="/assets/js/119.73d9e688.js"><link rel="prefetch" href="/assets/js/12.53aaa509.js"><link rel="prefetch" href="/assets/js/120.202a8b2e.js"><link rel="prefetch" href="/assets/js/121.ca91d59a.js"><link rel="prefetch" href="/assets/js/122.8804c0dd.js"><link rel="prefetch" href="/assets/js/123.ceada225.js"><link rel="prefetch" href="/assets/js/124.4b2718b7.js"><link rel="prefetch" href="/assets/js/125.72134235.js"><link rel="prefetch" href="/assets/js/126.167b162c.js"><link rel="prefetch" href="/assets/js/127.f8139c17.js"><link rel="prefetch" href="/assets/js/128.da64a377.js"><link rel="prefetch" href="/assets/js/129.c07963b1.js"><link rel="prefetch" href="/assets/js/13.52940164.js"><link rel="prefetch" href="/assets/js/130.f66fc8bb.js"><link rel="prefetch" href="/assets/js/131.a913c7dd.js"><link rel="prefetch" href="/assets/js/132.ea648654.js"><link rel="prefetch" href="/assets/js/133.074ca70e.js"><link rel="prefetch" href="/assets/js/134.dc79ddd8.js"><link rel="prefetch" href="/assets/js/135.c4516208.js"><link rel="prefetch" href="/assets/js/136.2546baf7.js"><link rel="prefetch" href="/assets/js/137.56454fc6.js"><link rel="prefetch" href="/assets/js/138.bdb002bf.js"><link rel="prefetch" href="/assets/js/139.40a2f9b5.js"><link rel="prefetch" href="/assets/js/14.ade1e74d.js"><link rel="prefetch" href="/assets/js/140.06b20427.js"><link rel="prefetch" href="/assets/js/141.8dd992e3.js"><link rel="prefetch" href="/assets/js/142.d9305485.js"><link rel="prefetch" href="/assets/js/143.41bf907e.js"><link rel="prefetch" href="/assets/js/144.c138960c.js"><link rel="prefetch" href="/assets/js/145.fc5b38c8.js"><link rel="prefetch" href="/assets/js/146.c9166b70.js"><link rel="prefetch" href="/assets/js/147.c31d8a01.js"><link rel="prefetch" href="/assets/js/148.5c0534ca.js"><link rel="prefetch" href="/assets/js/149.d2355ea2.js"><link rel="prefetch" href="/assets/js/15.970a8ca5.js"><link rel="prefetch" href="/assets/js/150.c1644898.js"><link rel="prefetch" href="/assets/js/151.b6e4adf0.js"><link rel="prefetch" href="/assets/js/152.7a5071aa.js"><link rel="prefetch" href="/assets/js/153.31abbc68.js"><link rel="prefetch" href="/assets/js/154.9c6bb079.js"><link rel="prefetch" href="/assets/js/155.e5506a40.js"><link rel="prefetch" href="/assets/js/156.992dab50.js"><link rel="prefetch" href="/assets/js/157.12a6ee84.js"><link rel="prefetch" href="/assets/js/158.84dc1045.js"><link rel="prefetch" href="/assets/js/159.fd2022b1.js"><link rel="prefetch" href="/assets/js/16.ff971500.js"><link rel="prefetch" href="/assets/js/160.8d8889cd.js"><link rel="prefetch" href="/assets/js/161.31797def.js"><link rel="prefetch" href="/assets/js/162.bfae7a75.js"><link rel="prefetch" href="/assets/js/163.e5e8094d.js"><link rel="prefetch" href="/assets/js/164.870f212c.js"><link rel="prefetch" href="/assets/js/165.8da20a31.js"><link rel="prefetch" href="/assets/js/166.950a187d.js"><link rel="prefetch" href="/assets/js/167.fd042c52.js"><link rel="prefetch" href="/assets/js/168.7d9f058d.js"><link rel="prefetch" href="/assets/js/169.205df80e.js"><link rel="prefetch" href="/assets/js/17.4b045b26.js"><link rel="prefetch" href="/assets/js/170.270108a3.js"><link rel="prefetch" href="/assets/js/171.9f03d27d.js"><link rel="prefetch" href="/assets/js/172.1882bac7.js"><link rel="prefetch" href="/assets/js/173.fc1fe7d4.js"><link rel="prefetch" href="/assets/js/174.d77c927b.js"><link rel="prefetch" href="/assets/js/175.019b9e83.js"><link rel="prefetch" href="/assets/js/176.5ce31bd8.js"><link rel="prefetch" href="/assets/js/177.9a2006f2.js"><link rel="prefetch" href="/assets/js/178.88265ac1.js"><link rel="prefetch" href="/assets/js/179.3205ce07.js"><link rel="prefetch" href="/assets/js/18.1cdd0514.js"><link rel="prefetch" href="/assets/js/180.2ff51f44.js"><link rel="prefetch" href="/assets/js/181.f792ce97.js"><link rel="prefetch" href="/assets/js/182.103328e1.js"><link rel="prefetch" href="/assets/js/183.40fde303.js"><link rel="prefetch" href="/assets/js/184.1f36ac78.js"><link rel="prefetch" href="/assets/js/185.cf3b1c00.js"><link rel="prefetch" href="/assets/js/186.f7981399.js"><link rel="prefetch" href="/assets/js/187.20fe296d.js"><link rel="prefetch" href="/assets/js/188.7b004fea.js"><link rel="prefetch" href="/assets/js/189.599ca2d9.js"><link rel="prefetch" href="/assets/js/19.5740e4d6.js"><link rel="prefetch" href="/assets/js/190.14087c07.js"><link rel="prefetch" href="/assets/js/191.b0f73f6d.js"><link rel="prefetch" href="/assets/js/192.9d171669.js"><link rel="prefetch" href="/assets/js/193.00975585.js"><link rel="prefetch" href="/assets/js/194.2c7c43b4.js"><link rel="prefetch" href="/assets/js/195.ff126254.js"><link rel="prefetch" href="/assets/js/196.6546ae1c.js"><link rel="prefetch" href="/assets/js/197.eedb67bb.js"><link rel="prefetch" href="/assets/js/198.acdcc22c.js"><link rel="prefetch" href="/assets/js/199.7993a6b2.js"><link rel="prefetch" href="/assets/js/20.8885a4fa.js"><link rel="prefetch" href="/assets/js/200.52f08765.js"><link rel="prefetch" href="/assets/js/201.b85ab10b.js"><link rel="prefetch" href="/assets/js/202.ccec1c85.js"><link rel="prefetch" href="/assets/js/203.3f50a6ed.js"><link rel="prefetch" href="/assets/js/204.f28b803b.js"><link rel="prefetch" href="/assets/js/205.c51c53fd.js"><link rel="prefetch" href="/assets/js/206.ba0aa8f6.js"><link rel="prefetch" href="/assets/js/207.0dce311a.js"><link rel="prefetch" href="/assets/js/208.0ea629a6.js"><link rel="prefetch" href="/assets/js/209.f48c3e71.js"><link rel="prefetch" href="/assets/js/21.0bb62db4.js"><link rel="prefetch" href="/assets/js/210.689db8f2.js"><link rel="prefetch" href="/assets/js/211.72bc0f1f.js"><link rel="prefetch" href="/assets/js/212.456135bd.js"><link rel="prefetch" href="/assets/js/213.d0ea50fc.js"><link rel="prefetch" href="/assets/js/214.1400da91.js"><link rel="prefetch" href="/assets/js/215.aeef3e5d.js"><link rel="prefetch" href="/assets/js/216.a60215af.js"><link rel="prefetch" href="/assets/js/217.d6cd8b69.js"><link rel="prefetch" href="/assets/js/218.1bd500fc.js"><link rel="prefetch" href="/assets/js/219.20faf611.js"><link rel="prefetch" href="/assets/js/22.3e8347a6.js"><link rel="prefetch" href="/assets/js/220.6714179b.js"><link rel="prefetch" href="/assets/js/221.fb5b643d.js"><link rel="prefetch" href="/assets/js/222.d96272e5.js"><link rel="prefetch" href="/assets/js/223.4cb7dc3d.js"><link rel="prefetch" href="/assets/js/224.500d94cc.js"><link rel="prefetch" href="/assets/js/225.d006c34e.js"><link rel="prefetch" href="/assets/js/226.73612150.js"><link rel="prefetch" href="/assets/js/227.b02db9d1.js"><link rel="prefetch" href="/assets/js/228.aad43ff2.js"><link rel="prefetch" href="/assets/js/229.65340802.js"><link rel="prefetch" href="/assets/js/23.ceb5a5ff.js"><link rel="prefetch" href="/assets/js/230.97cab104.js"><link rel="prefetch" href="/assets/js/231.8415349a.js"><link rel="prefetch" href="/assets/js/232.db6d2697.js"><link rel="prefetch" href="/assets/js/233.723d05b1.js"><link rel="prefetch" href="/assets/js/234.26ed5e94.js"><link rel="prefetch" href="/assets/js/235.a586122b.js"><link rel="prefetch" href="/assets/js/236.f802bda8.js"><link rel="prefetch" href="/assets/js/237.cc8767ad.js"><link rel="prefetch" href="/assets/js/238.6485459e.js"><link rel="prefetch" href="/assets/js/239.9fbf3a55.js"><link rel="prefetch" href="/assets/js/24.07267ac6.js"><link rel="prefetch" href="/assets/js/240.1188f244.js"><link rel="prefetch" href="/assets/js/241.cbfb154d.js"><link rel="prefetch" href="/assets/js/242.352cea5a.js"><link rel="prefetch" href="/assets/js/243.07947e1c.js"><link rel="prefetch" href="/assets/js/244.f5b26fa9.js"><link rel="prefetch" href="/assets/js/245.c6030e32.js"><link rel="prefetch" href="/assets/js/246.d3fe99f7.js"><link rel="prefetch" href="/assets/js/247.185ae95d.js"><link rel="prefetch" href="/assets/js/248.a5f1548c.js"><link rel="prefetch" href="/assets/js/249.19691113.js"><link rel="prefetch" href="/assets/js/25.15e1f641.js"><link rel="prefetch" href="/assets/js/250.72fa1d33.js"><link rel="prefetch" href="/assets/js/251.bedd56f7.js"><link rel="prefetch" href="/assets/js/252.32bbcc8b.js"><link rel="prefetch" href="/assets/js/253.b59e5846.js"><link rel="prefetch" href="/assets/js/254.c0e37e1a.js"><link rel="prefetch" href="/assets/js/255.850da3cc.js"><link rel="prefetch" href="/assets/js/256.952d9817.js"><link rel="prefetch" href="/assets/js/257.88667bb8.js"><link rel="prefetch" href="/assets/js/258.c5873c28.js"><link rel="prefetch" href="/assets/js/259.6ae1dcbc.js"><link rel="prefetch" href="/assets/js/26.35b2bbdc.js"><link rel="prefetch" href="/assets/js/260.cb940cc3.js"><link rel="prefetch" href="/assets/js/261.0686c3e9.js"><link rel="prefetch" href="/assets/js/262.d0719839.js"><link rel="prefetch" href="/assets/js/263.ae83ebc1.js"><link rel="prefetch" href="/assets/js/264.209ad8a3.js"><link rel="prefetch" href="/assets/js/265.be8d1ee8.js"><link rel="prefetch" href="/assets/js/266.c51bceaa.js"><link rel="prefetch" href="/assets/js/267.fe8af48a.js"><link rel="prefetch" href="/assets/js/268.e18ed861.js"><link rel="prefetch" href="/assets/js/269.7126031e.js"><link rel="prefetch" href="/assets/js/27.270fcb06.js"><link rel="prefetch" href="/assets/js/270.89ead106.js"><link rel="prefetch" href="/assets/js/271.0643d07a.js"><link rel="prefetch" href="/assets/js/272.32a8e4b0.js"><link rel="prefetch" href="/assets/js/273.9881da47.js"><link rel="prefetch" href="/assets/js/274.2de022ea.js"><link rel="prefetch" href="/assets/js/275.d9a4fe99.js"><link rel="prefetch" href="/assets/js/276.d33de2e9.js"><link rel="prefetch" href="/assets/js/277.1ec367ab.js"><link rel="prefetch" href="/assets/js/278.66ab7c11.js"><link rel="prefetch" href="/assets/js/279.ff51cd15.js"><link rel="prefetch" href="/assets/js/28.ef3f6db2.js"><link rel="prefetch" href="/assets/js/280.ef45ed5f.js"><link rel="prefetch" href="/assets/js/281.87f26b81.js"><link rel="prefetch" href="/assets/js/282.c8ef594b.js"><link rel="prefetch" href="/assets/js/283.15643091.js"><link rel="prefetch" href="/assets/js/284.c4f25b2e.js"><link rel="prefetch" href="/assets/js/285.2d16e4b6.js"><link rel="prefetch" href="/assets/js/286.44eba266.js"><link rel="prefetch" href="/assets/js/287.7613e62f.js"><link rel="prefetch" href="/assets/js/288.b9546d55.js"><link rel="prefetch" href="/assets/js/289.125f85d8.js"><link rel="prefetch" href="/assets/js/29.177558be.js"><link rel="prefetch" href="/assets/js/290.db375797.js"><link rel="prefetch" href="/assets/js/291.90946626.js"><link rel="prefetch" href="/assets/js/292.a4524678.js"><link rel="prefetch" href="/assets/js/293.3ed9b083.js"><link rel="prefetch" href="/assets/js/294.d6c14116.js"><link rel="prefetch" href="/assets/js/295.2b485032.js"><link rel="prefetch" href="/assets/js/296.29d9aa0f.js"><link rel="prefetch" href="/assets/js/297.bfd72435.js"><link rel="prefetch" href="/assets/js/298.fc056989.js"><link rel="prefetch" href="/assets/js/299.0a3f258e.js"><link rel="prefetch" href="/assets/js/3.4dc033c7.js"><link rel="prefetch" href="/assets/js/30.248a229d.js"><link rel="prefetch" href="/assets/js/300.d064d776.js"><link rel="prefetch" href="/assets/js/301.d95b9fc0.js"><link rel="prefetch" href="/assets/js/302.1afa637a.js"><link rel="prefetch" href="/assets/js/303.231beaa8.js"><link rel="prefetch" href="/assets/js/304.05df35d9.js"><link rel="prefetch" href="/assets/js/305.f2d83cf9.js"><link rel="prefetch" href="/assets/js/306.d54f5e5e.js"><link rel="prefetch" href="/assets/js/307.d5df9000.js"><link rel="prefetch" href="/assets/js/308.ef2f52b3.js"><link rel="prefetch" href="/assets/js/309.d1f13319.js"><link rel="prefetch" href="/assets/js/31.32a62b91.js"><link rel="prefetch" href="/assets/js/310.30b84be3.js"><link rel="prefetch" href="/assets/js/311.d79e3145.js"><link rel="prefetch" href="/assets/js/312.58e24f19.js"><link rel="prefetch" href="/assets/js/313.9809a122.js"><link rel="prefetch" href="/assets/js/314.e92e41b1.js"><link rel="prefetch" href="/assets/js/315.8c2fc1d4.js"><link rel="prefetch" href="/assets/js/316.a0904343.js"><link rel="prefetch" href="/assets/js/317.18e42b54.js"><link rel="prefetch" href="/assets/js/318.6b051d27.js"><link rel="prefetch" href="/assets/js/319.513b0197.js"><link rel="prefetch" href="/assets/js/32.23b75afc.js"><link rel="prefetch" href="/assets/js/320.ae91bc36.js"><link rel="prefetch" href="/assets/js/321.a194efea.js"><link rel="prefetch" href="/assets/js/322.8273020e.js"><link rel="prefetch" href="/assets/js/323.d332e08f.js"><link rel="prefetch" href="/assets/js/324.ed867b64.js"><link rel="prefetch" href="/assets/js/325.c43ccf9a.js"><link rel="prefetch" href="/assets/js/326.9497d879.js"><link rel="prefetch" href="/assets/js/327.7f095e40.js"><link rel="prefetch" href="/assets/js/328.cb5c5847.js"><link rel="prefetch" href="/assets/js/329.d36b59f3.js"><link rel="prefetch" href="/assets/js/33.b258b779.js"><link rel="prefetch" href="/assets/js/330.b1a092b9.js"><link rel="prefetch" href="/assets/js/331.ff86566f.js"><link rel="prefetch" href="/assets/js/332.0daafa9f.js"><link rel="prefetch" href="/assets/js/333.b7e50524.js"><link rel="prefetch" href="/assets/js/334.7dcf9f0b.js"><link rel="prefetch" href="/assets/js/335.862c410a.js"><link rel="prefetch" href="/assets/js/336.c6384990.js"><link rel="prefetch" href="/assets/js/337.ab9d5e52.js"><link rel="prefetch" href="/assets/js/338.598ae59e.js"><link rel="prefetch" href="/assets/js/339.9b810ff8.js"><link rel="prefetch" href="/assets/js/34.3bd60f1f.js"><link rel="prefetch" href="/assets/js/340.5609a53c.js"><link rel="prefetch" href="/assets/js/341.969d335a.js"><link rel="prefetch" href="/assets/js/342.43f85dba.js"><link rel="prefetch" href="/assets/js/343.47280ef9.js"><link rel="prefetch" href="/assets/js/344.346ae5fc.js"><link rel="prefetch" href="/assets/js/345.32166361.js"><link rel="prefetch" href="/assets/js/346.86ff128b.js"><link rel="prefetch" href="/assets/js/347.3c5421fe.js"><link rel="prefetch" href="/assets/js/348.95910300.js"><link rel="prefetch" href="/assets/js/349.004158d8.js"><link rel="prefetch" href="/assets/js/35.18e8f66a.js"><link rel="prefetch" href="/assets/js/350.e10b195b.js"><link rel="prefetch" href="/assets/js/351.12f89875.js"><link rel="prefetch" href="/assets/js/352.83957394.js"><link rel="prefetch" href="/assets/js/353.475971b9.js"><link rel="prefetch" href="/assets/js/354.8af7b26b.js"><link rel="prefetch" href="/assets/js/355.85925e24.js"><link rel="prefetch" href="/assets/js/356.1d77cf9c.js"><link rel="prefetch" href="/assets/js/357.b467d481.js"><link rel="prefetch" href="/assets/js/358.ac96f32d.js"><link rel="prefetch" href="/assets/js/359.e048bd10.js"><link rel="prefetch" href="/assets/js/36.a7ae257c.js"><link rel="prefetch" href="/assets/js/360.e9b45545.js"><link rel="prefetch" href="/assets/js/361.ad1bb45b.js"><link rel="prefetch" href="/assets/js/362.4e24a30b.js"><link rel="prefetch" href="/assets/js/363.64f7dad5.js"><link rel="prefetch" href="/assets/js/364.d9e3ebc2.js"><link rel="prefetch" href="/assets/js/365.330e3086.js"><link rel="prefetch" href="/assets/js/366.a6c0afb3.js"><link rel="prefetch" href="/assets/js/367.ffcefa40.js"><link rel="prefetch" href="/assets/js/368.664c8e4d.js"><link rel="prefetch" href="/assets/js/369.67da2dd2.js"><link rel="prefetch" href="/assets/js/37.d709f9a9.js"><link rel="prefetch" href="/assets/js/370.b1791970.js"><link rel="prefetch" href="/assets/js/371.743a461a.js"><link rel="prefetch" href="/assets/js/372.7703ef1b.js"><link rel="prefetch" href="/assets/js/373.17de31f6.js"><link rel="prefetch" href="/assets/js/374.e508be9e.js"><link rel="prefetch" href="/assets/js/375.6e2e9fe3.js"><link rel="prefetch" href="/assets/js/376.8ca3511a.js"><link rel="prefetch" href="/assets/js/377.bfcee39c.js"><link rel="prefetch" href="/assets/js/378.21852b78.js"><link rel="prefetch" href="/assets/js/379.9649c307.js"><link rel="prefetch" href="/assets/js/38.68ca920e.js"><link rel="prefetch" href="/assets/js/380.dadb4418.js"><link rel="prefetch" href="/assets/js/381.a03c993d.js"><link rel="prefetch" href="/assets/js/382.bb7c22c1.js"><link rel="prefetch" href="/assets/js/383.bd68b2e5.js"><link rel="prefetch" href="/assets/js/384.f6dc7457.js"><link rel="prefetch" href="/assets/js/385.14287a91.js"><link rel="prefetch" href="/assets/js/386.a6284ac2.js"><link rel="prefetch" href="/assets/js/387.c51f147a.js"><link rel="prefetch" href="/assets/js/388.06d9651e.js"><link rel="prefetch" href="/assets/js/389.2d85d927.js"><link rel="prefetch" href="/assets/js/39.de850db9.js"><link rel="prefetch" href="/assets/js/390.2397b3e0.js"><link rel="prefetch" href="/assets/js/391.8413aaaf.js"><link rel="prefetch" href="/assets/js/392.00eecaa4.js"><link rel="prefetch" href="/assets/js/393.351dd3fd.js"><link rel="prefetch" href="/assets/js/394.d2cc4a70.js"><link rel="prefetch" href="/assets/js/395.abee64bf.js"><link rel="prefetch" href="/assets/js/396.bc265e6a.js"><link rel="prefetch" href="/assets/js/397.e4a96944.js"><link rel="prefetch" href="/assets/js/398.f0d26b29.js"><link rel="prefetch" href="/assets/js/399.e77c3ddc.js"><link rel="prefetch" href="/assets/js/4.2f502b73.js"><link rel="prefetch" href="/assets/js/40.91635261.js"><link rel="prefetch" href="/assets/js/400.c7eac401.js"><link rel="prefetch" href="/assets/js/401.06092d16.js"><link rel="prefetch" href="/assets/js/402.4e08e496.js"><link rel="prefetch" href="/assets/js/403.deba4c77.js"><link rel="prefetch" href="/assets/js/404.9100e4df.js"><link rel="prefetch" href="/assets/js/405.e9f451e2.js"><link rel="prefetch" href="/assets/js/406.a0da4aa4.js"><link rel="prefetch" href="/assets/js/407.58b2b123.js"><link rel="prefetch" href="/assets/js/408.eb2cde2d.js"><link rel="prefetch" href="/assets/js/409.9041f749.js"><link rel="prefetch" href="/assets/js/41.169a3cfc.js"><link rel="prefetch" href="/assets/js/410.c12f3710.js"><link rel="prefetch" href="/assets/js/411.452cda45.js"><link rel="prefetch" href="/assets/js/412.8691317a.js"><link rel="prefetch" href="/assets/js/413.25a68f21.js"><link rel="prefetch" href="/assets/js/414.7a9699d8.js"><link rel="prefetch" href="/assets/js/415.6e8cdaff.js"><link rel="prefetch" href="/assets/js/416.e8a86529.js"><link rel="prefetch" href="/assets/js/417.28330bde.js"><link rel="prefetch" href="/assets/js/418.2fed8e17.js"><link rel="prefetch" href="/assets/js/419.773032f4.js"><link rel="prefetch" href="/assets/js/42.e17cd061.js"><link rel="prefetch" href="/assets/js/420.56681228.js"><link rel="prefetch" href="/assets/js/421.0d170e13.js"><link rel="prefetch" href="/assets/js/422.fc8c2a11.js"><link rel="prefetch" href="/assets/js/423.e34ea6a6.js"><link rel="prefetch" href="/assets/js/424.20fe8748.js"><link rel="prefetch" href="/assets/js/425.f001b7cd.js"><link rel="prefetch" href="/assets/js/426.a91230ef.js"><link rel="prefetch" href="/assets/js/427.c6c9fe8d.js"><link rel="prefetch" href="/assets/js/428.9d4f5e41.js"><link rel="prefetch" href="/assets/js/429.c81cfd3f.js"><link rel="prefetch" href="/assets/js/43.f88aa667.js"><link rel="prefetch" href="/assets/js/430.eb61372f.js"><link rel="prefetch" href="/assets/js/431.4a25365a.js"><link rel="prefetch" href="/assets/js/432.42fc1bbe.js"><link rel="prefetch" href="/assets/js/433.8e9ac9f0.js"><link rel="prefetch" href="/assets/js/434.fdd0b160.js"><link rel="prefetch" href="/assets/js/435.d6cbac31.js"><link rel="prefetch" href="/assets/js/436.17750ba2.js"><link rel="prefetch" href="/assets/js/437.a1468099.js"><link rel="prefetch" href="/assets/js/438.ac6a45c2.js"><link rel="prefetch" href="/assets/js/439.242ae27a.js"><link rel="prefetch" href="/assets/js/44.3f36e228.js"><link rel="prefetch" href="/assets/js/440.b80b520c.js"><link rel="prefetch" href="/assets/js/441.e51a4bbb.js"><link rel="prefetch" href="/assets/js/442.ab0b3eda.js"><link rel="prefetch" href="/assets/js/443.47e44250.js"><link rel="prefetch" href="/assets/js/444.54c3b425.js"><link rel="prefetch" href="/assets/js/445.e006c12e.js"><link rel="prefetch" href="/assets/js/446.f2b79730.js"><link rel="prefetch" href="/assets/js/447.2509a397.js"><link rel="prefetch" href="/assets/js/448.b7d69dfc.js"><link rel="prefetch" href="/assets/js/449.8a575d9b.js"><link rel="prefetch" href="/assets/js/45.be1455b1.js"><link rel="prefetch" href="/assets/js/450.ad421aeb.js"><link rel="prefetch" href="/assets/js/451.f09912ae.js"><link rel="prefetch" href="/assets/js/452.f528c4ce.js"><link rel="prefetch" href="/assets/js/453.fc4dc0f9.js"><link rel="prefetch" href="/assets/js/454.733c315c.js"><link rel="prefetch" href="/assets/js/455.44fe35b1.js"><link rel="prefetch" href="/assets/js/456.7f650035.js"><link rel="prefetch" href="/assets/js/457.77ebc183.js"><link rel="prefetch" href="/assets/js/458.133078f4.js"><link rel="prefetch" href="/assets/js/459.33728b5e.js"><link rel="prefetch" href="/assets/js/46.5ad74cb5.js"><link rel="prefetch" href="/assets/js/460.6946f564.js"><link rel="prefetch" href="/assets/js/461.6f906c5c.js"><link rel="prefetch" href="/assets/js/462.26eca27e.js"><link rel="prefetch" href="/assets/js/463.e02e5f65.js"><link rel="prefetch" href="/assets/js/464.0df25c63.js"><link rel="prefetch" href="/assets/js/465.80cd249e.js"><link rel="prefetch" href="/assets/js/466.a0921e86.js"><link rel="prefetch" href="/assets/js/467.1bec2b47.js"><link rel="prefetch" href="/assets/js/468.b6fa37c1.js"><link rel="prefetch" href="/assets/js/469.a1d2487e.js"><link rel="prefetch" href="/assets/js/47.03df5d0a.js"><link rel="prefetch" href="/assets/js/470.36a4b9c8.js"><link rel="prefetch" href="/assets/js/471.28895273.js"><link rel="prefetch" href="/assets/js/472.b9fe392f.js"><link rel="prefetch" href="/assets/js/473.a2835d9c.js"><link rel="prefetch" href="/assets/js/474.cd57b37d.js"><link rel="prefetch" href="/assets/js/475.8d5a9792.js"><link rel="prefetch" href="/assets/js/476.f7f3781c.js"><link rel="prefetch" href="/assets/js/477.9b710b04.js"><link rel="prefetch" href="/assets/js/478.46d66687.js"><link rel="prefetch" href="/assets/js/479.e7444705.js"><link rel="prefetch" href="/assets/js/48.48f286ba.js"><link rel="prefetch" href="/assets/js/480.da2b750a.js"><link rel="prefetch" href="/assets/js/481.838a8e04.js"><link rel="prefetch" href="/assets/js/482.02bf8041.js"><link rel="prefetch" href="/assets/js/483.bb29ef2f.js"><link rel="prefetch" href="/assets/js/484.aebb68bd.js"><link rel="prefetch" href="/assets/js/485.a66ab41b.js"><link rel="prefetch" href="/assets/js/486.8df516fb.js"><link rel="prefetch" href="/assets/js/487.86d28130.js"><link rel="prefetch" href="/assets/js/488.0863da70.js"><link rel="prefetch" href="/assets/js/489.5d4d9e88.js"><link rel="prefetch" href="/assets/js/49.1ca52d11.js"><link rel="prefetch" href="/assets/js/490.3fd65d85.js"><link rel="prefetch" href="/assets/js/491.ae3a2f87.js"><link rel="prefetch" href="/assets/js/492.6f54679b.js"><link rel="prefetch" href="/assets/js/493.a594b1f4.js"><link rel="prefetch" href="/assets/js/494.4f03fae1.js"><link rel="prefetch" href="/assets/js/495.ae50b66b.js"><link rel="prefetch" href="/assets/js/496.bc92d835.js"><link rel="prefetch" href="/assets/js/497.eac65251.js"><link rel="prefetch" href="/assets/js/498.1d726726.js"><link rel="prefetch" href="/assets/js/499.01e4f7d1.js"><link rel="prefetch" href="/assets/js/5.7b1f056c.js"><link rel="prefetch" href="/assets/js/50.73732fe2.js"><link rel="prefetch" href="/assets/js/500.062ade4e.js"><link rel="prefetch" href="/assets/js/501.e6a711dc.js"><link rel="prefetch" href="/assets/js/502.c79e28e2.js"><link rel="prefetch" href="/assets/js/503.bf97bce9.js"><link rel="prefetch" href="/assets/js/504.b2784ef0.js"><link rel="prefetch" href="/assets/js/505.0767ba54.js"><link rel="prefetch" href="/assets/js/506.8ae14637.js"><link rel="prefetch" href="/assets/js/507.003e2349.js"><link rel="prefetch" href="/assets/js/508.ecb5e6bd.js"><link rel="prefetch" href="/assets/js/509.b644f44a.js"><link rel="prefetch" href="/assets/js/51.bed7cf61.js"><link rel="prefetch" href="/assets/js/510.b660946f.js"><link rel="prefetch" href="/assets/js/511.cb186c8f.js"><link rel="prefetch" href="/assets/js/512.d176170f.js"><link rel="prefetch" href="/assets/js/513.18a1afa5.js"><link rel="prefetch" href="/assets/js/514.4a33e931.js"><link rel="prefetch" href="/assets/js/515.8496bd4d.js"><link rel="prefetch" href="/assets/js/516.529b9476.js"><link rel="prefetch" href="/assets/js/517.289738f1.js"><link rel="prefetch" href="/assets/js/518.eda39556.js"><link rel="prefetch" href="/assets/js/519.85b42de5.js"><link rel="prefetch" href="/assets/js/52.22a7da62.js"><link rel="prefetch" href="/assets/js/520.8b531775.js"><link rel="prefetch" href="/assets/js/521.8e4b66fb.js"><link rel="prefetch" href="/assets/js/522.050bfc55.js"><link rel="prefetch" href="/assets/js/523.e973f1e0.js"><link rel="prefetch" href="/assets/js/524.fd160738.js"><link rel="prefetch" href="/assets/js/525.5a4bc307.js"><link rel="prefetch" href="/assets/js/526.361bc4fa.js"><link rel="prefetch" href="/assets/js/527.06ee9d73.js"><link rel="prefetch" href="/assets/js/528.22d9b118.js"><link rel="prefetch" href="/assets/js/529.b7592c16.js"><link rel="prefetch" href="/assets/js/53.767f3bbf.js"><link rel="prefetch" href="/assets/js/530.ad96d564.js"><link rel="prefetch" href="/assets/js/531.61905243.js"><link rel="prefetch" href="/assets/js/532.11aebf61.js"><link rel="prefetch" href="/assets/js/533.0cddb226.js"><link rel="prefetch" href="/assets/js/534.d1c9cc36.js"><link rel="prefetch" href="/assets/js/54.d1f9eec8.js"><link rel="prefetch" href="/assets/js/55.aa90e812.js"><link rel="prefetch" href="/assets/js/56.4d7f81f2.js"><link rel="prefetch" href="/assets/js/57.3c540e8d.js"><link rel="prefetch" href="/assets/js/58.fc1c23f2.js"><link rel="prefetch" href="/assets/js/59.c7a7a4c0.js"><link rel="prefetch" href="/assets/js/6.056886f1.js"><link rel="prefetch" href="/assets/js/60.40e590ae.js"><link rel="prefetch" href="/assets/js/61.e400b323.js"><link rel="prefetch" href="/assets/js/63.c31cd2bb.js"><link rel="prefetch" href="/assets/js/64.62321188.js"><link rel="prefetch" href="/assets/js/65.44adc2b3.js"><link rel="prefetch" href="/assets/js/66.b5740d4f.js"><link rel="prefetch" href="/assets/js/67.76c3624c.js"><link rel="prefetch" href="/assets/js/68.6dd982d4.js"><link rel="prefetch" href="/assets/js/69.eb1390ec.js"><link rel="prefetch" href="/assets/js/7.a96cecf9.js"><link rel="prefetch" href="/assets/js/70.8c882bdd.js"><link rel="prefetch" href="/assets/js/71.001c968a.js"><link rel="prefetch" href="/assets/js/72.ae4ca075.js"><link rel="prefetch" href="/assets/js/73.d6ae6a27.js"><link rel="prefetch" href="/assets/js/74.fba60e37.js"><link rel="prefetch" href="/assets/js/75.0b1bf7bf.js"><link rel="prefetch" href="/assets/js/76.ee19d90e.js"><link rel="prefetch" href="/assets/js/77.fe8ac5f0.js"><link rel="prefetch" href="/assets/js/78.ae09432e.js"><link rel="prefetch" href="/assets/js/79.c1b10e2d.js"><link rel="prefetch" href="/assets/js/80.ae809538.js"><link rel="prefetch" href="/assets/js/81.ca11b4c7.js"><link rel="prefetch" href="/assets/js/82.308b98e4.js"><link rel="prefetch" href="/assets/js/83.355d9dde.js"><link rel="prefetch" href="/assets/js/84.a2b61d48.js"><link rel="prefetch" href="/assets/js/85.cf992fda.js"><link rel="prefetch" href="/assets/js/86.f547c7ea.js"><link rel="prefetch" href="/assets/js/87.2b02746a.js"><link rel="prefetch" href="/assets/js/88.e939aad5.js"><link rel="prefetch" href="/assets/js/89.b000c42a.js"><link rel="prefetch" href="/assets/js/90.dadef001.js"><link rel="prefetch" href="/assets/js/91.c0fe2e32.js"><link rel="prefetch" href="/assets/js/92.e09650b9.js"><link rel="prefetch" href="/assets/js/93.427c7280.js"><link rel="prefetch" href="/assets/js/94.90fc5f58.js"><link rel="prefetch" href="/assets/js/95.13d42b3a.js"><link rel="prefetch" href="/assets/js/96.d0a2ea8c.js"><link rel="prefetch" href="/assets/js/97.cc9a0eda.js"><link rel="prefetch" href="/assets/js/98.3c9f1b7e.js"><link rel="prefetch" href="/assets/js/99.0420dcbc.js"><link rel="prefetch" href="/assets/js/vendors~docsearch.33b2b47d.js">
    <link rel="stylesheet" href="/assets/css/0.styles.40c15e22.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><img src="/logo.png" alt="鱼皮的编程宝典" class="logo"> <span class="site-name can-hide">鱼皮的编程宝典</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/学习路线/" class="nav-link">
  学习路线
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="自学之路" class="dropdown-title"><span class="title">自学之路</span> <span class="arrow down"></span></button> <button type="button" aria-label="自学之路" class="mobile-dropdown-title"><span class="title">自学之路</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/自学之路/#大学经历" class="nav-link">
  大学经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#求职经历" class="nav-link">
  求职经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#职场工作" class="nav-link">
  职场工作
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创作经历" class="nav-link">
  创作经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创业经历" class="nav-link">
  创业经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#生活日常" class="nav-link">
  生活日常
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="编程分享" class="dropdown-title"><span class="title">编程分享</span> <span class="arrow down"></span></button> <button type="button" aria-label="编程分享" class="mobile-dropdown-title"><span class="title">编程分享</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/编程分享/#入门必看-学习路线" class="nav-link">
  入门必看-学习路线
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#学习指南" class="nav-link">
  学习指南
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#开发经验" class="nav-link">
  开发经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#求职经验" class="nav-link">
  求职经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#职场经验" class="nav-link">
  职场经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#技术分享" class="nav-link">
  技术分享
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#实战教程" class="nav-link">
  实战教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#其他" class="nav-link">
  其他
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#📚-项目教程" class="nav-link">
  项目教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#个人作品" class="nav-link">
  个人作品
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🎁-编程资源" class="nav-link">
  编程资源
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🌐-科技科普" class="nav-link">
  科技科普
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="项目实战" class="dropdown-title"><span class="title">项目实战</span> <span class="arrow down"></span></button> <button type="button" aria-label="项目实战" class="mobile-dropdown-title"><span class="title">项目实战</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/项目实战/OJ 判题系统.html" class="nav-link">
  OJ 判题系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/智能 BI 平台.html" class="nav-link">
  智能 BI 平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/聚合搜索平台.html" class="nav-link">
  聚合搜索平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/API 开放平台.html" class="nav-link">
  API 开放平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/伙伴匹配系统.html" class="nav-link">
  伙伴匹配系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/用户中心项目.html" class="nav-link">
  用户中心项目
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/Java 后端万用项目模板.html" class="nav-link">
  Java 后端万用项目模板
</a></li></ul></div></div><div class="nav-item"><a href="/知识碎片/" class="nav-link">
  知识碎片
</a></div><div class="nav-item"><a href="/编程导航/" class="nav-link">
  🔥 编程导航
</a></div><div class="nav-item"><a href="/产品服务/" class="nav-link">
  产品服务
</a></div><div class="nav-item"><a href="/作者/" class="nav-link">
  作者
</a></div> <a href="https://github.com/liyupi/codefather" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/学习路线/" class="nav-link">
  学习路线
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="自学之路" class="dropdown-title"><span class="title">自学之路</span> <span class="arrow down"></span></button> <button type="button" aria-label="自学之路" class="mobile-dropdown-title"><span class="title">自学之路</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/自学之路/#大学经历" class="nav-link">
  大学经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#求职经历" class="nav-link">
  求职经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#职场工作" class="nav-link">
  职场工作
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创作经历" class="nav-link">
  创作经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创业经历" class="nav-link">
  创业经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#生活日常" class="nav-link">
  生活日常
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="编程分享" class="dropdown-title"><span class="title">编程分享</span> <span class="arrow down"></span></button> <button type="button" aria-label="编程分享" class="mobile-dropdown-title"><span class="title">编程分享</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/编程分享/#入门必看-学习路线" class="nav-link">
  入门必看-学习路线
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#学习指南" class="nav-link">
  学习指南
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#开发经验" class="nav-link">
  开发经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#求职经验" class="nav-link">
  求职经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#职场经验" class="nav-link">
  职场经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#技术分享" class="nav-link">
  技术分享
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#实战教程" class="nav-link">
  实战教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#其他" class="nav-link">
  其他
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#📚-项目教程" class="nav-link">
  项目教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#个人作品" class="nav-link">
  个人作品
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🎁-编程资源" class="nav-link">
  编程资源
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🌐-科技科普" class="nav-link">
  科技科普
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="项目实战" class="dropdown-title"><span class="title">项目实战</span> <span class="arrow down"></span></button> <button type="button" aria-label="项目实战" class="mobile-dropdown-title"><span class="title">项目实战</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/项目实战/OJ 判题系统.html" class="nav-link">
  OJ 判题系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/智能 BI 平台.html" class="nav-link">
  智能 BI 平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/聚合搜索平台.html" class="nav-link">
  聚合搜索平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/API 开放平台.html" class="nav-link">
  API 开放平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/伙伴匹配系统.html" class="nav-link">
  伙伴匹配系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/用户中心项目.html" class="nav-link">
  用户中心项目
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/Java 后端万用项目模板.html" class="nav-link">
  Java 后端万用项目模板
</a></li></ul></div></div><div class="nav-item"><a href="/知识碎片/" class="nav-link">
  知识碎片
</a></div><div class="nav-item"><a href="/编程导航/" class="nav-link">
  🔥 编程导航
</a></div><div class="nav-item"><a href="/产品服务/" class="nav-link">
  产品服务
</a></div><div class="nav-item"><a href="/作者/" class="nav-link">
  作者
</a></div> <a href="https://github.com/liyupi/codefather" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><a href="/%E7%9F%A5%E8%AF%86%E7%A2%8E%E7%89%87/" class="sidebar-link">编程导航知识碎片</a></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading open"><span>知识碎片</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/_4-%E7%A7%8D%E6%96%B9%E6%B3%95-%E5%BF%AB%E9%80%9F%E5%88%9D%E5%A7%8B%E5%8C%96-java-%E9%A1%B9%E7%9B%AE/" class="sidebar-link">4 种方法，快速初始化 Java 项目</a></li><li><a href="/%E4%BD%BF%E7%94%A8%E5%90%8E%E7%AB%AF%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%99%A8-%E6%8F%90%E9%AB%98%E5%BC%80%E5%8F%91%E6%95%88%E7%8E%87/" class="sidebar-link">使用后端代码生成器，提高开发效率</a></li><li><a href="/%E5%89%8D%E7%AB%AF%E5%BF%85%E5%AD%A6%E7%9A%84%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6-ant-design-pro/" class="sidebar-link">前端必学的开发框架，Ant Design Pro</a></li><li><a href="/%E5%90%8E%E7%AB%AF%E5%A6%82%E4%BD%95%E7%BC%96%E5%86%99%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/" class="sidebar-link">后端如何编写单元测试？</a></li><li><a href="/%E5%90%8E%E7%AB%AF%E9%A1%B9%E7%9B%AE%E7%BB%8F%E5%85%B8%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E4%BB%8B%E7%BB%8D/" class="sidebar-link">后端项目经典分层架构介绍</a></li><li><a href="/%E5%A6%82%E4%BD%95%E9%AB%98%E6%95%88%E6%B5%8B%E8%AF%95%E6%8E%A5%E5%8F%A3-%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3/" class="sidebar-link">如何高效测试接口？自动生成接口文档</a></li><li><a href="/%E5%BC%80%E5%8F%91%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E7%BE%A4%E6%9C%BA%E5%99%A8%E4%BA%BA-%E5%AE%9E%E7%8E%B0%E5%AE%9A%E6%97%B6%E6%8F%90%E9%86%92/" class="sidebar-link">开发企业微信群机器人，实现定时提醒</a></li><li><a href="/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3%E6%95%B0%E6%8D%AE%E6%9F%A5%E8%AF%A2%E4%B8%BA%E7%A9%BA%E7%9A%84-bug/" class="sidebar-link">如何解决数据查询为空的 Bug？</a></li><li><a href="/git-%E6%8F%90%E4%BA%A4%E7%AB%9F%E7%84%B6%E8%BF%98%E8%83%BD%E8%BF%99%E4%B9%88%E7%94%A8/" class="sidebar-link">Git 提交竟然还能这么用？</a></li><li><a href="/%E3%80%90%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%91%E8%A3%85%E9%A5%B0%E8%80%85%E6%A8%A1%E5%BC%8F-%E5%8F%8A%E5%85%B6%E5%9C%A8jdk%E6%BA%90%E7%A0%81%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/" class="sidebar-link">【设计模式】装饰者模式，及其在JDK源码中的应用</a></li><li><a href="/%E7%94%A8%E5%9B%9B%E7%A7%8D%E9%94%81%E5%AE%9E%E7%8E%B0%E5%8A%A0%E5%85%A5%E9%98%9F%E4%BC%8D%E5%8A%9F%E8%83%BD-%E4%BC%99%E4%BC%B4%E5%8C%B9%E9%85%8D%E7%B3%BB%E7%BB%9F/" class="sidebar-link">用四种锁实现加入队伍功能(伙伴匹配系统)</a></li><li><a href="/%E7%94%A8-validated%E6%B3%A8%E8%A7%A3%E5%AE%9E%E7%8E%B0%E9%9D%9E%E7%A9%BA%E6%A0%A1%E9%AA%8C/" class="sidebar-link">用@Validated注解实现非空校验</a></li><li><a href="/%E4%BD%BF%E7%94%A8%E7%AC%AC%E4%B8%89%E6%96%B9%E6%9C%8D%E5%8A%A1-%E5%AE%9D%E5%A1%94-%E5%BF%AB%E9%80%9F%E9%83%A8%E7%BD%B2%E9%A1%B9%E7%9B%AE/" class="sidebar-link">使用第三方服务（宝塔）快速部署项目</a></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/" aria-current="page" class="active sidebar-link">RESTful 接口实现与测试( Spring Boot )</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#一、定义pojo对象" class="sidebar-link">一、定义pojo对象</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#二、定义http方法和controller" class="sidebar-link">二、定义HTTP方法和Controller</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#测试一下" class="sidebar-link">测试一下：</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#一、-requestparam注解对应的axios传参方法" class="sidebar-link">一、@RequestParam注解对应的axios传参方法</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#二、-requestbody的axios传参方法" class="sidebar-link">二、@RequestBody的axios传参方法</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#常用注解-2" class="sidebar-link">常用注解</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#bug" class="sidebar-link">bug</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#junit测试框架" class="sidebar-link">junit测试框架</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#mockito测试框架" class="sidebar-link">Mockito测试框架</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#真实servlet容器环境下的测试" class="sidebar-link">真实servlet容器环境下的测试</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#mock测试" class="sidebar-link">Mock测试</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#轻量级测试" class="sidebar-link">轻量级测试</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#mockmvc更多的用法总结" class="sidebar-link">MockMvc更多的用法总结</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#为什么需要api接口文档" class="sidebar-link">为什么需要API接口文档?</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#整合swagger2生成文档" class="sidebar-link">整合swagger2生成文档</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#书写swagger注解" class="sidebar-link">书写swagger注解</a></li><li class="sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#生产环境下如何禁用swagger2" class="sidebar-link">生产环境下如何禁用swagger2</a></li></ul></li><li><a href="/%E5%BF%AB%E9%80%9F%E5%AD%A6%E4%BC%9A%E4%B8%BA%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%81%9A%E8%B4%A1%E7%8C%AE/" class="sidebar-link">快速学会为开源项目做贡献</a></li><li><a href="/%E6%95%B4%E5%90%88spring-jdbc%E6%93%8D%E4%BD%9C%E6%95%B0%E6%8D%AE%E5%BA%93/" class="sidebar-link">整合Spring JDBC操作数据库</a></li><li><a href="/maven-%E4%B8%AD%E5%A4%AE%E4%BB%93%E5%BA%93%E5%8F%91%E5%8C%85%E6%B5%81%E7%A8%8B/" class="sidebar-link">Maven 中央仓库发包流程</a></li><li><a href="/nacos-%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83%E6%90%AD%E5%BB%BA/" class="sidebar-link">Nacos 配置中心搭建</a></li><li><a href="/bean-%E6%8B%B7%E8%B4%9D%E4%B9%8B-mapstruct/" class="sidebar-link">Bean 拷贝之 MapStruct</a></li><li><a href="/%E4%BD%BF%E7%94%A8-freemarker-%E6%A8%A1%E6%9D%BF%E5%BC%95%E6%93%8E%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81/" class="sidebar-link">使用 FreeMarker 模板引擎生成代码</a></li><li><a href="/%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E5%BF%BD%E7%95%A5-git-%E6%96%87%E4%BB%B6%E6%8F%90%E4%BA%A4/" class="sidebar-link">如何快速忽略 Git 文件提交？</a></li><li><a href="/easyexcel-%E5%AF%BC%E5%87%BA%E6%96%87%E4%BB%B6/" class="sidebar-link">EasyExcel 导出文件</a></li><li><a href="/%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86-completablefuture-%E4%B8%AD%E7%9A%84%E4%B8%A4%E7%A7%8D%E5%BC%82%E5%B8%B8-%E6%99%BA%E8%83%BDbi%E9%A1%B9%E7%9B%AE/" class="sidebar-link">如何处理 CompletableFuture 中的两种异常(智能BI项目)</a></li><li><a href="/%E5%9F%BA%E4%BA%8E-session-%E5%AE%9E%E7%8E%B0%E7%9F%AD%E4%BF%A1%E7%99%BB%E5%BD%95/" class="sidebar-link">基于 Session 实现短信登录</a></li><li><a href="/synchronized-%E5%85%B3%E9%94%AE%E5%AD%97%E8%AF%A6%E8%A7%A3/" class="sidebar-link">Synchronized 关键字详解</a></li><li><a href="/redission-%E8%A7%A3%E9%94%81%E5%BC%82%E5%B8%B8%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/" class="sidebar-link">Redission 解锁异常解决方案</a></li><li><a href="/%E5%AE%9E%E7%8E%B0%E4%B8%BB%E9%A2%98%E6%8D%A2%E8%82%A4-css-%E5%8F%98%E9%87%8F-%E7%B1%BB%E5%90%8D%E5%88%87%E6%8D%A2/" class="sidebar-link">实现主题换肤( CSS 变量 + 类名切换)</a></li><li><a href="/springboot-%E6%95%B4%E5%90%88-minio%E5%85%A8%E6%B5%81%E7%A8%8B-%E4%BB%8E%E5%AE%89%E8%A3%85-minio-%E5%88%B0%E5%BA%94%E7%94%A8/" class="sidebar-link">SpringBoot 整合 Minio全流程（从安装 Minio 到应用）</a></li><li><a href="/%E3%80%90redis%E5%BA%94%E7%94%A8%E3%80%91uv%E7%BB%9F%E8%AE%A1/" class="sidebar-link">【Redis应用】UV统计</a></li><li><a href="/%E3%80%8A%E8%B4%AF%E7%A9%BF%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%8B%E7%AC%AC%E4%B8%89%E6%96%B9%E7%99%BB%E5%BD%95%E5%AE%9E%E8%B7%B5-%E9%99%84%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%8C%E6%95%B4%E8%BF%87%E7%A8%8B%E5%8F%8A%E6%B5%8B%E8%AF%95/" class="sidebar-link">《贯穿设计模式》第三方登录实践[附前后端完整过程及测试]</a></li><li><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/" class="sidebar-link">Websocket.+ Spring-SseEmitter3 实现讯飞星火Java客户端</a></li><li><a href="/%E7%94%A8%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E5%AE%9E%E7%8E%B0%E6%A3%80%E6%9F%A5%E7%94%A8%E6%88%B7%E5%90%8D%E6%98%AF%E5%90%A6%E5%AD%98%E5%9C%A8-%E7%94%A8%E6%88%B7%E6%B3%A8%E5%86%8C/" class="sidebar-link">用布隆过滤器实现检查用户名是否存在(用户注册)</a></li><li><a href="/springsecurity-%E6%95%B4%E5%90%88-oauth2/" class="sidebar-link">SpringSecurity 整合 Oauth2</a></li><li><a href="/%E5%89%8D%E7%AB%AF%E5%88%9D%E5%A7%8B%E5%8C%96-ant-design-pro-%E7%AC%94%E8%AE%B0/" class="sidebar-link">前端初始化 Ant Design Pro 笔记</a></li><li><a href="/liteflow-%E7%BC%96%E6%8E%92%E5%BC%8F%E7%BC%96%E7%A8%8B%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D/" class="sidebar-link">LiteFlow 编排式编程简单介绍</a></li><li><a href="/%E5%9F%BA%E4%BA%8E-redis-%E5%AE%9E%E7%8E%B0%E7%9F%AD%E4%BF%A1%E7%99%BB%E5%BD%95/" class="sidebar-link">基于 Redis 实现短信登录</a></li><li><a href="/mysql-%E4%B8%80%E6%A3%B5-b-%E6%A0%91%E5%8F%AF%E4%BB%A5%E5%AD%98%E5%A4%9A%E5%B0%91%E6%9D%A1%E6%95%B0%E6%8D%AE/" class="sidebar-link">MySQL 一棵 B + 树可以存多少条数据？</a></li><li><a href="/springboot-%E6%95%B4%E5%90%88-elk-%E5%AE%9E%E7%8E%B0%E6%97%A5%E5%BF%97%E9%87%87%E9%9B%86%E4%B8%8E%E7%9B%91%E6%8E%A7/" class="sidebar-link">SpringBoot 整合 ELK 实现日志采集与监控</a></li><li><a href="/%E5%9F%BA%E4%BA%8E%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3%E7%9A%84-redisson-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%AE%9E%E7%8E%B0/" class="sidebar-link">基于自定义注解的 Redisson 分布式锁实现</a></li><li><a href="/%E7%8E%A9%E8%BD%AC%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B%E5%88%A9%E5%99%A8-completablefuture/" class="sidebar-link">玩转异步编程利器 CompletableFuture</a></li><li><a href="/%E9%80%9A%E8%BF%87-windows-%E8%84%9A%E6%9C%AC%E6%89%A7%E8%A1%8C%E6%89%93%E5%8C%85%E6%93%8D%E4%BD%9C/" class="sidebar-link">通过 Windows 脚本执行打包操作</a></li><li><a href="/%E9%98%BF%E9%87%8C%E4%BA%91%E5%AF%B9%E8%B1%A1%E5%AD%98%E5%82%A8-oss/" class="sidebar-link">阿里云对象存储 OSS</a></li><li><a href="/%E5%90%8E%E7%AB%AF-spring-boot-%E4%B8%87%E7%94%A8%E6%A8%A1%E6%9D%BF%E4%BD%BF%E7%94%A8/" class="sidebar-link">后端 Spring Boot 万用模板使用</a></li><li><a href="/%E4%BD%BF%E7%94%A8-canal-%E5%AE%9E%E7%8E%B0%E5%A2%9E%E9%87%8F%E8%AE%A2%E9%98%85%E5%92%8C%E6%B6%88%E8%B4%B9/" class="sidebar-link">使用 canal 实现增量订阅和消费</a></li><li><a href="/springboot-quartz-%E7%AE%80%E6%98%93%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1/" class="sidebar-link">SpringBoot + Quartz 简易定时任务</a></li><li><a href="/string-%E7%9A%84-intern-%E6%96%B9%E6%B3%95/" class="sidebar-link">String 的 intern() 方法</a></li><li><a href="/mysql-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%97%A5%E6%9C%9F%E6%A0%BC%E5%BC%8F%E8%BD%AC%E6%8D%A2/" class="sidebar-link">MySQL 字符串日期格式转换</a></li><li><a href="/fail-fast-%E6%9C%BA%E5%88%B6%E6%98%AF%E4%BB%80%E4%B9%88/" class="sidebar-link">fail-fast 机制是什么？</a></li><li><a href="/%E5%8D%81%E7%A7%8D-sql-%E7%9A%84%E5%86%99%E6%B3%95/" class="sidebar-link">十种 SQL 的写法</a></li><li><a href="/mybatis-%E6%95%B4%E5%90%88%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90/" class="sidebar-link">MyBatis 整合多数据源</a></li><li><a href="/_1%E7%A7%92%E5%B0%86%E6%9C%AC%E5%9C%B0springboot%E9%A1%B9%E7%9B%AEjar%E5%8C%85%E9%83%A8%E7%BD%B2%E5%88%B0linux%E7%8E%AF%E5%A2%83/" class="sidebar-link">1秒将本地SpringBoot项目jar包部署到Linux环境</a></li><li><a href="/ddd-%E6%98%AF%E4%BB%80%E4%B9%88/" class="sidebar-link">DDD 是什么？</a></li><li><a href="/oracle%E5%88%B0mysql%E5%87%BD%E6%95%B0%E6%9B%BF%E6%8D%A2%E6%96%B9%E6%A1%88%E6%B1%87%E6%80%BB/" class="sidebar-link">Oracle 到 MySQL 函数替换方案汇总</a></li><li><a href="/springboot-%E5%BC%95%E5%85%A5-nacos-windows-%E7%89%88/" class="sidebar-link">Springboot 引入 Nacos ( Windows 版)</a></li><li><a href="/%E5%88%A9%E7%94%A8%E5%B1%80%E5%9F%9F%E7%BD%91%E6%90%AD%E5%BB%BA%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AE%9E%E7%8E%B0%E8%AE%BF%E9%97%AE%E9%A1%B9%E7%9B%AE/" class="sidebar-link">利用局域网搭建虚拟机实现访问项目</a></li><li><a href="/%E6%B5%85%E8%B0%88-cookie-%E5%92%8C-session/" class="sidebar-link">浅谈 cookie 和 session</a></li><li><a href="/%E7%94%A8-qq-%E9%82%AE%E7%AE%B1%E5%AE%9E%E7%8E%B0%E9%AA%8C%E8%AF%81%E7%A0%81%E5%8A%9F%E8%83%BD/" class="sidebar-link">用 QQ 邮箱实现验证码功能</a></li><li><a href="/redis-%E5%AE%9E%E7%8E%B0%E6%96%87%E7%AB%A0%E7%82%B9%E8%B5%9E%E5%8A%9F%E8%83%BD-%E9%99%84%E5%B8%A6%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BB%A3%E7%A0%81%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/" class="sidebar-link">Redis 实现文章点赞功能(附带前后端代码、数据库)</a></li><li><a href="/%E9%98%BF%E9%87%8C%E4%BA%91%E7%9F%AD%E4%BF%A1%E6%9C%8D%E5%8A%A1%E5%AE%9E%E7%8E%B0%E6%89%8B%E6%9C%BA%E9%AA%8C%E8%AF%81%E7%A0%81/" class="sidebar-link">阿里云短信服务实现手机验证码</a></li><li><a href="/%E3%80%90ajax%E3%80%91%E5%BC%82%E6%AD%A5%E9%80%9A%E4%BF%A1/" class="sidebar-link">【Ajax】异步通信</a></li><li><a href="/%E3%80%90%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%91%E4%B8%83%E5%A4%A7%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/" class="sidebar-link">【设计模式】七大设计原则</a></li><li><a href="/%E6%96%87%E7%AB%A0%E8%AF%84%E8%AE%BA%E5%8A%9F%E8%83%BD%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%A1%88%E6%80%BB%E7%BB%93/" class="sidebar-link">文章评论功能前后端实现方案总结</a></li><li><a href="/%E3%80%90%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6%E3%80%91git%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B/" class="sidebar-link">【版本控制】Git快速上手</a></li><li><a href="/%E3%80%90java%E5%9F%BA%E7%A1%80%E3%80%91%E6%B6%88%E7%81%AD%E9%AD%94%E6%B3%95%E5%80%BC-%E5%B8%B8%E9%87%8F-%E6%9E%9A%E4%B8%BE%E8%AF%A6%E8%BF%B0/" class="sidebar-link">【Java基础】消灭魔法值-常量&amp;枚举详述</a></li><li><a href="/mysql-%E7%9A%84-char-%E5%B9%B6%E4%B8%8D%E4%B8%80%E5%AE%9A%E6%98%AF%E5%AE%9A%E9%95%BF/" class="sidebar-link">MySQL 的 Char 并不一定是定长</a></li><li><a href="/textcnn-%E6%96%87%E6%9C%AC%E5%88%86%E7%B1%BB%E6%A8%A1%E5%9E%8B%E5%9C%A8%E6%96%87%E7%AB%A0%E8%AF%84%E8%AE%BA%E5%AE%A1%E6%A0%B8%E4%B8%AD%E7%9A%84%E5%AE%9E%E7%8E%B0%E4%B8%8E%E9%83%A8%E7%BD%B2/" class="sidebar-link">TextCNN 文本分类模型在文章评论审核中的实现与部署</a></li><li><a href="/%E7%90%86%E8%A7%A3-io-%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8/" class="sidebar-link">理解 IO 多路复用</a></li><li><a href="/elasticsearch-%E5%9F%BA%E7%A1%80%E6%A6%82%E5%BF%B5%E4%B8%8E%E5%85%A5%E9%97%A8%E4%BD%BF%E7%94%A8/" class="sidebar-link">ElasticSearch 基础概念与入门使用</a></li><li><a href="/%E7%90%86%E8%A7%A3%E8%BF%9B%E7%A8%8B-%E7%BA%BF%E7%A8%8B-%E5%8D%8F%E7%A8%8B/" class="sidebar-link">理解进程，线程，协程</a></li><li><a href="/%E7%90%86%E8%A7%A3%E5%8E%9F%E7%A0%81%E3%80%81%E5%8F%8D%E7%A0%81%E3%80%81%E8%A1%A5%E7%A0%81/" class="sidebar-link">理解原码、反码、补码</a></li><li><a href="/%E5%AE%9A%E4%B9%89api%E5%B9%B6%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81-go-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%A1%86%E6%9E%B6-kratos/" class="sidebar-link">定义API并生成代码（ Go 微服务框架 Kratos ）</a></li></ul></section></li></ul> </aside> <main class="page"> <div class="content"><div style="width:100%"><div class="theme-default-content custom-content content__default"><h1 id="restful-接口实现与测试-spring-boot"><a href="#restful-接口实现与测试-spring-boot" class="header-anchor">#</a> RESTful 接口实现与测试( Spring Boot )</h1> <blockquote><p>作者：<a href="https://wx.zsxq.com/dweb2/index/footprint/418844844821828" target="_blank" rel="noopener noreferrer">无题.<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，<a href="https://wx.zsxq.com/dweb2/index/group/51122858222824" target="_blank" rel="noopener noreferrer">编程导航星球<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 编号 4838</p></blockquote> <p>本文将教你彻底理解如何使用spring boot开发一个RESTful接口与如何进行接口测试</p> <h1 id="是什么"><a href="#是什么" class="header-anchor">#</a> 是什么？</h1> <p><strong>RESTful是基于http方法的API设计风格，而不是一种新的技术</strong></p> <ol><li>看Url就知道要什么资源</li> <li>看http method就知道针对资源干什么</li> <li>看http status code就知道结果如何</li></ol> <p><strong>REST 通过 URI 暴露资源时，会强调不要在 URI 中出现动词</strong></p> <h1 id="设计风格"><a href="#设计风格" class="header-anchor">#</a> 设计风格</h1> <p>关于HTTP RESTful风格API设计的更多例子，参考：http://httpbin.org/</p> <h1 id="http协议四种传参方式"><a href="#http协议四种传参方式" class="header-anchor">#</a> HTTP协议四种传参方式</h1> <table><thead><tr><th>HTTP协议组成</th> <th>协议内容示例</th> <th>对应Spring注解</th></tr></thead> <tbody><tr><td>path info传参</td> <td>/articles/12 (查询id为12的文章，12是参数)</td> <td>@PathVariable</td></tr> <tr><td>URL Query String传参</td> <td>/articles?id=12</td> <td>@RequestParam</td></tr> <tr><td>Body 传参</td> <td>Content-Type: multipart/form-data</td> <td>@RequestParam</td></tr> <tr><td>Body 传参</td> <td>Content-Type: application/json，或其他自定义格式</td> <td>@RequestBody</td></tr> <tr><td>Headers 传参</td> <td></td> <td>@RequestHeader</td></tr></tbody></table> <h1 id="常用注解"><a href="#常用注解" class="header-anchor">#</a> 常用注解</h1> <h3 id="requestbody与-responsebody"><a href="#requestbody与-responsebody" class="header-anchor">#</a> @RequestBody与@ResponseBody</h3> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token comment">//注意并不要求@RequestBody与@ResponseBody成对使用。</span>
<span class="token keyword">public</span> <span class="token annotation punctuation">@ResponseBody</span>  <span class="token class-name">AjaxResponse</span> <span class="token function">saveArticle</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">ArticleVO</span> article<span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>如上代码所示：</p> <ul><li>@RequestBody修饰请求参数，注解用于接收HTTP的body，默认是使用JSON的格式</li> <li>@ResponseBody修饰返回值，注解用于在HTTP的body中携带响应数据，默认是使用JSON的格式。如果不加该注解，spring响应字符串类型，是跳转到模板页面或jsp页面的开发模式。说白了：加上这个注解你开发的是一个数据接口，不加这个注解你开发的是一个页面跳转控制器。</li></ul> <p>在使用@ResponseBody注解之后程序不会再走视图解析器，也就不再做html视图渲染，而是直接将对象以数据的形式（默认JSON）返回给请求发送者。那么我们有一个问题：如果我们想接收或XML数据该怎么办？我们想响应excel的数据格式该怎么办？</p> <h3 id="requestmapping注解"><a href="#requestmapping注解" class="header-anchor">#</a> @RequestMapping注解</h3> <p>用于标注HTTP服务端点。它的很多属性对于丰富我们的应用开发方式方法，都有很重要的作用。如：</p> <ul><li>value： 应用请求端点，最核心的属性，用于标志请求处理方法的唯一性；</li> <li>method： HTTP协议的method类型， 如：GET、POST、PUT、DELETE等；</li> <li>consumes： HTTP协议请求内容的数据类型（Content-Type），例如application/json, text/html;</li> <li>produces: HTTP协议响应内容的数据类型。下文会详细讲解。</li> <li>params： HTTP请求中必须包含某些参数值的时候，才允许被注解标注的方法处理请求。</li> <li>headers： HTTP请求中必须包含某些指定的header值，才允许被注解标注的方法处理请求。</li></ul> <p><img alt="image-20231104190556172" data-src="https://pic.yupi.icu/5563/image-20231104190556172.png" loading="lazy" class="lazy"></p> <h3 id="restcontroller与-controller"><a href="#restcontroller与-controller" class="header-anchor">#</a> @RestController与@Controller</h3> <p>@Controller注解是开发中最常使用的注解，它的作用有两层含义：</p> <ul><li>一是告诉Spring，<strong>被该注解标注的类是一个Spring的Bean</strong>，需要被注入到Spring的上下文环境中。</li> <li>二是该类里面所有被RequestMapping标注的注解都是HTTP服务端点。</li></ul> <p>@RestController相当于 @Controller和@ResponseBody结合。它有两层含义：</p> <ul><li>一是作为Controller的作用，将控制器类注入到Spring上下文环境，该类RequestMapping标注方法为HTTP服务端点。</li> <li>二是作为ResponseBody的作用，<strong>请求响应默认使用的序列化方式是JSON</strong>，而不是跳转到jsp或模板页面。</li></ul> <h3 id="pathvariable-与-requestparam"><a href="#pathvariable-与-requestparam" class="header-anchor">#</a> @PathVariable 与@RequestParam</h3> <ul><li>PathVariable<strong>用于URI上的{参数}</strong>，如下方法用于删除一篇文章，其中id为文章id。如：我们的请求URL为“/article/1”,那么将匹配DeleteMapping并且PathVariable接收参数id=1。</li> <li>RequestParam用于**接收普通表单方式或者ajax模拟表单提交的参数数据。**如果使用了这个注解，但是前端没有传入参数，就会报错</li></ul> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@DeleteMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/article/{id}&quot;</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token annotation punctuation">@ResponseBody</span> <span class="token class-name">AjaxResponse</span> <span class="token function">deleteArticle</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/article&quot;</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token annotation punctuation">@ResponseBody</span> <span class="token class-name">AjaxResponse</span> <span class="token function">deleteArticle</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestParam</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p><img alt="image-20231104190632522" data-src="https://pic.yupi.icu/5563/image-20231104190632522.png" loading="lazy" class="lazy"></p> <h1 id="接受复杂嵌套对象参数"><a href="#接受复杂嵌套对象参数" class="header-anchor">#</a> 接受复杂嵌套对象参数</h1> <p><strong>RequestBody注解</strong>的真正意义在于能够使用对象或者嵌套对象接收前端数据。</p> <p>一个paramData对象里面包含了一个bestFriend对象。这种数据结构使用RequestParam就无法接收了，RequestParam只能接收平面的、一对一的参数。像上文中这种数据结构的参数，就需要我们在java服务端定义两个类，一个类是ParamData，一个类是BestFriend</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ParamData</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">int</span> id<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> phone<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">BestFriend</span> bestFriend<span class="token punctuation">;</span>
    
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">BestFriend</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token class-name">String</span> address<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token class-name">String</span> sex<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><ul><li>注意上面代码中省略了GET、SET方法等必要的java plain model元素。</li> <li>注意<strong>成员变量名称一定要和JSON属性名称对应上。</strong></li> <li>注意接收不同类型的参数，使用不同的成员变量类型</li></ul> <p>完成以上动作，我们就可以使用<code>@RequestBody ParamData paramData</code>，一次性的接收以上所有的复杂嵌套对象参数了，参数对象的所有属性都将被赋值。</p> <h1 id="http数据转换的原理"><a href="#http数据转换的原理" class="header-anchor">#</a> Http数据转换的原理</h1> <p>使用JSON都比较普遍了，其方便易用、表达能力强，是绝大部分数据接口式应用的首选。那么如何响应其他的类型的数据？其中的判别原理又是什么？下面就来给大家介绍一下：</p> <p><img alt="image-20231104190647009" data-src="https://pic.yupi.icu/5563/image-20231104190647009.png" loading="lazy" class="lazy"></p> <ul><li>当一个HTTP请求到达时是一个InputStream，通过HttpMessageConverter转换为java对象，从而进行参数接收。</li> <li>当对一个HTTP请求进行响应时，我们首先输出的是一个java对象，然后由HttpMessageConverter转换为OutputStream输出。</li></ul> <p>当我们在Spring Boot应用中集成了<strong>jackson的类库</strong>之后，如下的一些HttpMessageConverter将会被加载。</p> <p><img alt="image-20231104190658630" data-src="https://pic.yupi.icu/5563/image-20231104190658630.png" loading="lazy" class="lazy">根据HTTP协议的Accept和Content-Type属性，以及参数数据类型来判别使用哪一种HttpMessageConverter。**当使用RequestBody或ResponseBody时，再结合前端发送的Accept数据类型，会自动判定优先使用MappingJacksonHttpMessageConverter作为数据转换器。**但是，不仅JSON可以表达对象数据类型，XML也可以。<strong>如果我们希望使用XML格式该怎么告知Spring呢，那就要使用到produces属性了。</strong></p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span>value <span class="token operator">=</span><span class="token string">&quot;/demo&quot;</span><span class="token punctuation">,</span>produces <span class="token operator">=</span> <span class="token class-name">MediaType</span><span class="token punctuation">.</span><span class="token constant">APPLICATION_XML_VALUE</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>这里我们明确的告知了返回的数据类型是xml，就会使用Jaxb2RootElementHttpMessageConverter作为默认的数据转换器。当然实现XML数据响应比JSON还会更复杂一些，还需要结合@XmlRootElement、@XmlElement等注解实体类来使用。</p> <h1 id="自定义httpmessageconverter"><a href="#自定义httpmessageconverter" class="header-anchor">#</a> 自定义HttpMessageConverter</h1> <p>其实绝大多数的数据格式都不需要我们自定义HttpMessageConverter，都有第三方类库可以帮助我们实现(包括下文代码中的Excel格式)。但有的时候，有些数据的输出格式并没有类似于Jackson这种类库帮助我们处理，需要我们自定义数据格式。该怎么做?</p> <p>下面我们就以Excel数据格式为例，<strong>写一个自定义的HTTP类型转换器</strong>。实现的效果就是，当我们返回AjaxResponse这种数据类型的话，就自动将AjaxResponse转成Excel数据响应给客户端。</p> <p>引入依赖</p> <div class="language-XML line-numbers-mode"><pre class="language-xml"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">&gt;</span></span>
   <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">&gt;</span></span>org.apache.poi<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">&gt;</span></span>
   <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">&gt;</span></span>poi-ooxml<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">&gt;</span></span>
   <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">&gt;</span></span>3.9<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">&gt;</span></span>
@Service
public class ResponseToXlsConverter extends AbstractHttpMessageConverter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>AjaxResponse</span><span class="token punctuation">&gt;</span></span> {

    private static final MediaType EXCEL_TYPE = MediaType.valueOf(&quot;application/vnd.ms-excel&quot;);

    ResponseToXlsConverter() {
        super(EXCEL_TYPE);
    }

    @Override
    protected AjaxResponse readInternal(final Class<span class="token prolog">&lt;? extends AjaxResponse&gt; clazz,
                                final HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        return null;
    }

    //针对AjaxResponse类型返回值，使用下面的writeInternal方法进行消息类型转换
    @Override
    protected boolean supports(final Class&lt;?&gt;</span> clazz) {
        return (AjaxResponse.class == clazz);
    }

    @Override
    protected void writeInternal(final AjaxResponse ajaxResponse, final HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {

        final Workbook workbook = new HSSFWorkbook();
        final Sheet sheet = workbook.createSheet();

        final Row row = sheet.createRow(0);
        row.createCell(0).setCellValue(ajaxResponse.getMessage());
        row.createCell(1).setCellValue(ajaxResponse.getData().toString());

        workbook.write(outputMessage.getBody());
    }
}
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br></div></div><ul><li>实现AbstractHttpMessageConverter接口</li> <li>指定该转换器是针对哪种数据格式的？如上文代码中的&quot;application/vnd.ms-excel&quot;</li> <li>指定该转换器针对那些对象数据类型？如上文代码中的supports函数</li> <li>使用writeInternal对数据进行输出处理，上例中是输出为Excel格式。</li></ul> <p><img alt="image-20231104190721012" data-src="https://pic.yupi.icu/5563/image-20231104190721012.png" loading="lazy" class="lazy"></p> <p>注意这是要把自定义的Http转换器加上@Service注解</p> <p><code>@Service</code>注解是Spring框架中的一个注解，用于标识一个类作为服务组件。当类被标记为<code>@Service</code>时，Spring会自动将其识别为一个服务，并进行相关的依赖注入和管理。</p> <h1 id="统一规划接口响应的数据格式"><a href="#统一规划接口响应的数据格式" class="header-anchor">#</a> 统一规划接口响应的数据格式</h1> <p>下面这个类是用于统一数据响应接口标准的。它的作用是：统一所有开发人员响应前端请求的返回结果格式，减少前后端开发人员沟通成本，是一种RESTful接口标准化的开发约定。</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Data</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AjaxResponse</span> <span class="token punctuation">{</span>

  <span class="token keyword">private</span> <span class="token keyword">boolean</span> isok<span class="token punctuation">;</span>  <span class="token comment">//请求是否处理成功</span>
  <span class="token keyword">private</span> <span class="token keyword">int</span> code<span class="token punctuation">;</span> <span class="token comment">//请求响应状态码（200、400、500）</span>
  <span class="token keyword">private</span> <span class="token class-name">String</span> message<span class="token punctuation">;</span>  <span class="token comment">//请求结果描述信息</span>
  <span class="token keyword">private</span> <span class="token class-name">Object</span> data<span class="token punctuation">;</span> <span class="token comment">//请求结果数据（通常用于查询操作）</span>

  <span class="token keyword">private</span> <span class="token class-name">AjaxResponse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">}</span>

  <span class="token comment">//请求成功的响应，不带查询数据（用于删除、修改、新增接口）</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">AjaxResponse</span> <span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token class-name">AjaxResponse</span> ajaxResponse <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AjaxResponse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    ajaxResponse<span class="token punctuation">.</span><span class="token function">setIsok</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    ajaxResponse<span class="token punctuation">.</span><span class="token function">setCode</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    ajaxResponse<span class="token punctuation">.</span><span class="token function">setMessage</span><span class="token punctuation">(</span><span class="token string">&quot;请求响应成功!&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> ajaxResponse<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token comment">//请求成功的响应，带有查询数据（用于数据查询接口）</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">AjaxResponse</span> <span class="token function">success</span><span class="token punctuation">(</span><span class="token class-name">Object</span> obj<span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token class-name">AjaxResponse</span> ajaxResponse <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AjaxResponse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    ajaxResponse<span class="token punctuation">.</span><span class="token function">setIsok</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    ajaxResponse<span class="token punctuation">.</span><span class="token function">setCode</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    ajaxResponse<span class="token punctuation">.</span><span class="token function">setMessage</span><span class="token punctuation">(</span><span class="token string">&quot;请求响应成功!&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    ajaxResponse<span class="token punctuation">.</span><span class="token function">setData</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> ajaxResponse<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token comment">//请求成功的响应，带有查询数据（用于数据查询接口）</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">AjaxResponse</span> <span class="token function">success</span><span class="token punctuation">(</span><span class="token class-name">Object</span> obj<span class="token punctuation">,</span><span class="token class-name">String</span> message<span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token class-name">AjaxResponse</span> ajaxResponse <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AjaxResponse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    ajaxResponse<span class="token punctuation">.</span><span class="token function">setIsok</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    ajaxResponse<span class="token punctuation">.</span><span class="token function">setCode</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    ajaxResponse<span class="token punctuation">.</span><span class="token function">setMessage</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
    ajaxResponse<span class="token punctuation">.</span><span class="token function">setData</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> ajaxResponse<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>


<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br></div></div><h1 id="实战-使用注解开发一个rest接口"><a href="#实战-使用注解开发一个rest接口" class="header-anchor">#</a> 实战：使用注解开发一个REST接口</h1> <h2 id="一、定义pojo对象"><a href="#一、定义pojo对象" class="header-anchor">#</a> 一、定义pojo对象</h2> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Data</span>
<span class="token annotation punctuation">@Builder</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Article</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token class-name">Long</span> id<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> author<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> title<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> content<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">Data</span> createTime<span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Reader</span><span class="token punctuation">&gt;</span></span> reader<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token annotation punctuation">@Data</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Reader</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">Integer</span> age<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br></div></div><ul><li>@Builder为我们提供了通过对象属性的链式赋值构建对象的方法</li> <li>@Data注解帮我们定义了一系列常用方法，如：getters、setters、hashcode、equals等</li></ul> <h2 id="二、定义http方法和controller"><a href="#二、定义http方法和controller" class="header-anchor">#</a> 二、定义HTTP方法和Controller</h2> <ul><li>增加一篇Article ，使用POST方法</li> <li>删除一篇Article，使用DELETE方法，参数是id</li> <li>更新一篇Article，使用PUT方法，以id为主键进行更新</li> <li>获取一篇Article，使用GET方法</li></ul> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Slf4j</span>
<span class="token annotation punctuation">@RestController</span>
<span class="token annotation punctuation">@RequestMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/rest&quot;</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ArticleController</span> <span class="token punctuation">{</span>
    <span class="token comment">//根据文章的Id查询一篇文章</span>
    <span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/article/{id}&quot;</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token class-name">AjaxResponse</span> <span class="token function">getArticleById</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">//使用Lombok提供的buidler构建对象(构造一些假数据）</span>
        <span class="token class-name">Article</span> article <span class="token operator">=</span> <span class="token class-name">Article</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">id</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">author</span><span class="token punctuation">(</span><span class="token string">&quot;lombok&quot;</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">content</span><span class="token punctuation">(</span><span class="token string">&quot;你好spring boot&quot;</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">createTime</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">title</span><span class="token punctuation">(</span><span class="token string">&quot;day01&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token class-name">AjaxResponse</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>article<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">//增加一篇Article ，使用POST方法(RequestBody方式接收参数)</span>
    <span class="token comment">//@RequestMapping(value = &quot;/articles&quot;,method = RequestMethod.POST)</span>
    <span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/articles&quot;</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token class-name">AjaxResponse</span> <span class="token function">saveArticle</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">Article</span> article<span class="token punctuation">,</span>
                                    <span class="token annotation punctuation">@RequestHeader</span> <span class="token class-name">String</span> aaa<span class="token punctuation">)</span><span class="token punctuation">{</span>

        <span class="token comment">//因为使用了lombok的Slf4j注解，这里可以直接使用log变量打印日志</span>
        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;saveArticle:&quot;</span> <span class="token operator">+</span> article<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token class-name">AjaxResponse</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">//增加一篇Article ，使用POST方法(RequestParam方式接收参数)</span>
  <span class="token comment">/*@PostMapping(&quot;/articles&quot;)
  public AjaxResponse saveArticle(@RequestParam  String author,
                                  @RequestParam  String title,
                                  @RequestParam  String content,
                                  @DateTimeFormat(pattern = &quot;yyyy-MM-dd HH:mm:ss&quot;)
                                  @RequestParam  Date createTime){

    log.info(&quot;saveArticle:&quot; + createTime);
    return AjaxResponse.success();
  }*/</span>


    <span class="token comment">//更新一篇Article，使用PUT方法，以id为主键进行更新</span>
    <span class="token comment">//@RequestMapping(value = &quot;/articles&quot;,method = RequestMethod.PUT)</span>
    <span class="token annotation punctuation">@PutMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/articles&quot;</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token class-name">AjaxResponse</span> <span class="token function">updateArticle</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">Article</span> article<span class="token punctuation">)</span><span class="token punctuation">{</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>article<span class="token punctuation">.</span><span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
            <span class="token comment">//article.id是必传参数，因为通常根据id去修改数据</span>
            <span class="token comment">//TODO 抛出一个自定义的异常</span>
        <span class="token punctuation">}</span>

        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;updateArticle:&quot;</span> <span class="token operator">+</span> article<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token class-name">AjaxResponse</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">//删除一篇Article，使用DELETE方法，参数是id</span>
    <span class="token comment">//@RequestMapping(value = &quot;/articles/{id}&quot;,method = RequestMethod.DELETE)</span>
    <span class="token annotation punctuation">@DeleteMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/articles/{id}&quot;</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token class-name">AjaxResponse</span> <span class="token function">deleteArticle</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span><span class="token punctuation">(</span><span class="token string">&quot;id&quot;</span><span class="token punctuation">)</span> <span class="token class-name">Long</span> id<span class="token punctuation">)</span><span class="token punctuation">{</span>

        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;deleteArticle:&quot;</span> <span class="token operator">+</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token class-name">AjaxResponse</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br><span class="line-number">60</span><br><span class="line-number">61</span><br><span class="line-number">62</span><br><span class="line-number">63</span><br></div></div><h2 id="测试一下"><a href="#测试一下" class="header-anchor">#</a> 测试一下：</h2> <p><img alt="image-20231104190737919" data-src="https://pic.yupi.icu/5563/image-20231104190737919.png" loading="lazy" class="lazy"></p> <h1 id="配合前端axios传参总结"><a href="#配合前端axios传参总结" class="header-anchor">#</a> 配合前端axios传参总结</h1> <ul><li><code>@RequestParam</code>注解，默认接收<code>Content-Type: application/x-www-form-urlencoded</code>编码格式的数据</li> <li><code>@RequestBody</code>注解，默认接收JSON类型格式的数据。</li></ul> <h2 id="一、-requestparam注解对应的axios传参方法"><a href="#一、-requestparam注解对应的axios传参方法" class="header-anchor">#</a> 一、<code>@RequestParam</code>注解对应的axios传参方法</h2> <p>以下面的这段Spring java代码为例，接口使用POST协议，需要接受的参数分别是tsCode、indexCols、table。针对这个Spring的HTTP接口，axios该如何传参？有几种方法？我们来一一介绍。</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/line&quot;</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span> <span class="token keyword">extends</span> <span class="token class-name">BaseEntity</span><span class="token punctuation">&gt;</span></span> <span class="token function">commonEChart</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestParam</span> <span class="token class-name">String</span> tsCode<span class="token punctuation">,</span>
                                     <span class="token annotation punctuation">@RequestParam</span> <span class="token class-name">String</span> indexCols<span class="token punctuation">,</span>
                                     <span class="token annotation punctuation">@RequestParam</span> <span class="token class-name">String</span> table<span class="token punctuation">)</span><span class="token punctuation">{</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><h3 id="params传参-推荐"><a href="#params传参-推荐" class="header-anchor">#</a> params传参（推荐）</h3> <p>使用axios实例的params进行传参，就会将params参数格式化为x-www-form-urlencoded的格式，<strong>与后端参数一一对应即可传参成功。</strong></p> <div class="language-JavaScript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">return</span> <span class="token function">request</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'/chart/line'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'post'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">params</span><span class="token operator">:</span> <span class="token punctuation">{</span>   <span class="token comment">//注意这里的key是params</span>
        tsCode<span class="token punctuation">,</span>
        indexCols<span class="token punctuation">,</span>
        table
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><h3 id="formdata传参"><a href="#formdata传参" class="header-anchor">#</a> FormData传参</h3> <p>还可以使用js的FormData对象进行参数格式化，同样可以在Spring后端正确的使用<code>@RequestParam</code>注解进行参数接收。</p> <div class="language-JavaScript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">let</span> params <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FormData</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
params<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'tsCode'</span><span class="token punctuation">,</span> tsCode<span class="token punctuation">)</span><span class="token punctuation">;</span>
params<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'indexCols'</span><span class="token punctuation">,</span> indexCols<span class="token punctuation">)</span><span class="token punctuation">;</span>
params<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'table'</span><span class="token punctuation">,</span> table<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token function">request</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'/chart/line'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'post'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">data</span><span class="token operator">:</span> params   <span class="token comment">//注意这里的key是data</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><h3 id="qs-stringfy传参"><a href="#qs-stringfy传参" class="header-anchor">#</a> qs.stringfy传参</h3> <p>还可以使用<code>qs.stringfy</code>进行参数格式化，同样可以在Spring后端正确的使用<code>@RequestParam</code>注解进行参数接收。</p> <div class="language-JavaScript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">import</span> qs <span class="token keyword">from</span> <span class="token string">&quot;qs&quot;</span><span class="token punctuation">;</span>

<span class="token keyword">return</span> <span class="token function">request</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'/chart/line'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'post'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">data</span><span class="token operator">:</span> qs<span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span>    <span class="token comment">//注意这里的key是data</span>
        tsCode<span class="token punctuation">,</span>
        indexCols<span class="token punctuation">,</span>
        table
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>需要注意的是使用这种方法，需要手动设置header(Content-Type)</p> <div class="language-JavaScript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">const</span> service <span class="token operator">=</span> axios<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token string-property property">&quot;Content-Type&quot;</span><span class="token operator">:</span> <span class="token string">&quot;application/x-www-form-urlencoded&quot;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><h2 id="二、-requestbody的axios传参方法"><a href="#二、-requestbody的axios传参方法" class="header-anchor">#</a> 二、<code>@RequestBody</code>的axios传参方法</h2> <p>DemoModel类是一个实体类，包含名称tsCode,indexCols,table三个字符串成员变量。接收到的JSON格式参数会自动为demo对象的成员变量赋值。</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/line&quot;</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span> <span class="token keyword">extends</span> <span class="token class-name">BaseEntity</span><span class="token punctuation">&gt;</span></span> <span class="token function">commonEChart</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestBody</span> <span class="token class-name">DemoModel</span> demo<span class="token punctuation">)</span><span class="token punctuation">{</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p><code>@RequestBody</code>注解，默认接收JSON类型格式的数据。<strong>在axios中默认data传参就会默认使用JSON数据格式</strong>，所以不用额外的特殊处理。</p> <div class="language-JavaScript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">return</span> <span class="token function">request</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'/chart/line'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">method</span><span class="token operator">:</span> <span class="token string">'post'</span><span class="token punctuation">,</span>
    <span class="token literal-property property">data</span><span class="token operator">:</span> <span class="token punctuation">{</span>    <span class="token comment">//注意这里的key是data</span>
        tsCode<span class="token punctuation">,</span>
        indexCols<span class="token punctuation">,</span>
        table
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><h1 id="fastjson、gson和jackson对比"><a href="#fastjson、gson和jackson对比" class="header-anchor">#</a> FastJSON、Gson和Jackson对比</h1> <p><strong>开源的Jackson</strong>：SpringBoot默认是使用Jackson作为JSON数据格式处理的类库，<strong>Jackson在各方面都比较优秀，所以不建议将Jackson替换为Gson或fastjson。</strong></p> <p><strong>Google的Gson</strong>：Gson是Google为满足内部需求开发的JSON数据处理类库，其核心结构非常简单，toJson与fromJson两个转换函数实现对象与JSON数据的转换，</p> <p><strong>阿里巴巴的FastJson</strong>：Fastjson是阿里巴巴开源的JSON数据处理类库，其主要特点是序列化速度快。当并发数据量越大的时候，越能体现出fastjson的优势。但是笔者觉得选择JSON处理类库，快并不是唯一需要考虑的因素，与数据库或磁盘IO相比，JSON数据序列化与反序列化的这点时间还不足以对软件性能产生比较大的影响。而且这个库会有一些版本安全问题，代码质量不高，在国外几乎没人使用。</p> <h1 id="在spring中注解方法使用jackson"><a href="#在spring中注解方法使用jackson" class="header-anchor">#</a> 在Spring中注解方法使用Jackson</h1> <p>jackson的主要作用就是序列化与反序列化。</p> <blockquote><p>什么叫序列化与反序列化？说白了就是把对象转成可传输、可存储的格式（json、xml、二进制、甚至自定义格式）叫做序列化。反序列化顾名思义。</p></blockquote> <ul><li>反序列化：在客户端将请求数据上传到服务端的时候，自动的处理JSON数据对象中的字符串、数字，将其转换为包含Date类型、Integer等类型的对象。</li> <li>序列化：按照指定的格式、顺序等将实体类对象转换为JSON字符串</li></ul> <p>下面就给大家介绍一下jackson的常用注解的使用方法，帮助我们进行序列化和反序列化工作。</p> <h2 id="常用注解-2"><a href="#常用注解-2" class="header-anchor">#</a> 常用注解</h2> <p>这些注解通常用于标注java实体类或实体类的属性。</p> <ul><li>@JsonPropertyOrder(value={&quot;pname1&quot;,&quot;pname2&quot;}) 改变子属性在JSON序列化中的默认定义的顺序。如：param1在先，param2在后。</li> <li>@JsonIgnore 加在属性上面，排除某个属性不做序列化与反序列化</li> <li>@JsonIgnoreProperties(ignoreUnknown = true)，将这个注解写在类上之后，就会忽略JSON字符串中存在，但实体类不存在的属性，不予赋值，也不会出现异常。</li> <li>@JsonIgnoreProperties({ &quot;xxx&quot;, &quot;yyyy&quot; }) 忽略某些属性不进行序列化</li> <li>@JsonProperty(anotherName) 为某个属性换一个名称，体现在JSON数据里面</li> <li>@JsonInclude(JsonInclude.Include.NON_NULL) 排除为空的元素不做序列化反序列化</li> <li>@JsonFormat(pattern = &quot;yyyy-MM-dd HH:mm:ss&quot;, timezone = &quot;GMT+8&quot;) 指定日期类型的属性格式</li></ul> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@JsonPropertyOrder</span><span class="token punctuation">(</span>value<span class="token operator">=</span><span class="token punctuation">{</span><span class="token string">&quot;content&quot;</span><span class="token punctuation">,</span><span class="token string">&quot;title&quot;</span><span class="token punctuation">}</span><span class="token punctuation">)</span>  
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Article</span> <span class="token punctuation">{</span>

    <span class="token annotation punctuation">@JsonIgnore</span>
    <span class="token keyword">private</span> <span class="token class-name">Long</span> id<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@JsonProperty</span><span class="token punctuation">(</span><span class="token string">&quot;auther&quot;</span><span class="token punctuation">)</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> author<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> title<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> content<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@JsonInclude</span><span class="token punctuation">(</span><span class="token class-name">JsonInclude<span class="token punctuation">.</span>Include</span><span class="token punctuation">.</span><span class="token constant">NON_NULL</span><span class="token punctuation">)</span>
    <span class="token annotation punctuation">@JsonFormat</span><span class="token punctuation">(</span>pattern <span class="token operator">=</span> <span class="token string">&quot;yyyy-MM-dd HH:mm:ss&quot;</span><span class="token punctuation">,</span> timezone <span class="token operator">=</span> <span class="token string">&quot;GMT+8&quot;</span><span class="token punctuation">)</span>
    <span class="token keyword">private</span> <span class="token class-name">Date</span> createTime<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Reader</span><span class="token punctuation">&gt;</span></span> reader<span class="token punctuation">;</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br></div></div><p>上文代码中对应的JSON数据格式可以为：</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token punctuation">{</span>
    auther <span class="token operator">:</span><span class="token string">&quot;&quot;</span><span class="token punctuation">,</span>
    content<span class="token operator">:</span><span class="token string">&quot;&quot;</span><span class="token punctuation">,</span>
    title<span class="token operator">:</span><span class="token string">&quot;&quot;</span><span class="token punctuation">,</span>
    createTime<span class="token operator">:</span><span class="token string">&quot;2013-11-3 12:12:12&quot;</span><span class="token punctuation">,</span>
    reader<span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">{</span><span class="token string">&quot;name&quot;</span><span class="token operator">:</span><span class="token string">&quot;xhl&quot;</span><span class="token punctuation">,</span><span class="token string">&quot;age&quot;</span><span class="token operator">:</span><span class="token number">18</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token string">&quot;name&quot;</span><span class="token operator">:</span><span class="token string">&quot;jng&quot;</span><span class="token punctuation">,</span><span class="token string">&quot;age&quot;</span><span class="token operator">:</span><span class="token number">19</span><span class="token punctuation">}</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><ul><li>因为定义了JsonPropertyOrder，content在先，title在后</li> <li>因为定义了JsonIgnore，id属性被忽略</li> <li>因为定义了JsonProperty，author属性变为auther</li> <li>因为定义了JsonInclude和JsonFormat，createTime不要为空，并且格式为 &quot;yyyy-MM-dd HH:mm:ss&quot;</li></ul> <p>通常会对日期类型转换，进行全局配置，而不是在每一个java bean里面配置</p> <div class="language-YAML line-numbers-mode"><pre class="language-yaml"><code><span class="token key atrule">spring</span><span class="token punctuation">:</span> 
    <span class="token key atrule">jackson</span><span class="token punctuation">:</span>
        <span class="token key atrule">date-format</span><span class="token punctuation">:</span> yyyy<span class="token punctuation">-</span>MM<span class="token punctuation">-</span>dd HH<span class="token punctuation">:</span>mm<span class="token punctuation">:</span>ss
        <span class="token key atrule">time-zone</span><span class="token punctuation">:</span> GMT+8
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>调试成功：</p> <p><img alt="image-20231104190808684" data-src="https://pic.yupi.icu/5563/image-20231104190808684.png" loading="lazy" class="lazy"></p> <h1 id="手写数据转换"><a href="#手写数据转换" class="header-anchor">#</a> 手写数据转换</h1> <p>除了在spring框架内实现自动的前后端JSON数据与java对象的转换，我们还可以使用jackson自己写代码进行转换。</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token comment">//jackson的ObjectMapper 转换对象</span>
<span class="token class-name">ObjectMapper</span> mapper <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ObjectMapper</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//将某个java对象转换为JSON字符串</span>
<span class="token class-name">String</span> jsonStr <span class="token operator">=</span> mapper<span class="token punctuation">.</span><span class="token function">writeValueAsString</span><span class="token punctuation">(</span>javaObj<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//将jsonStr转换为Ademo类的对象</span>
<span class="token class-name">Ademo</span> ademo <span class="token operator">=</span> mapper<span class="token punctuation">.</span><span class="token function">readValue</span><span class="token punctuation">(</span>jsonStr<span class="token punctuation">,</span> <span class="token class-name">Ademo</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>当JSON字符串代表的对象的字段多于类定义的字段时，使用readValue会抛出UnrecognizedPropertyException异常，在类的定义处加上<code>@JsonIgnoreProperties(ignoreUnknown = true)</code>可以解决这个问题。</p> <h1 id="postman测试"><a href="#postman测试" class="header-anchor">#</a> Postman测试</h1> <p>下面让我们结合postman对REST接口和Jackson做一下测试吧。Postman是接口测试过程中经常使用到的工具。
测试使用数据:</p> <div class="language-JSON line-numbers-mode"><pre class="language-json"><code><span class="token punctuation">{</span>
    <span class="token property">&quot;id&quot;</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
    <span class="token property">&quot;author&quot;</span><span class="token operator">:</span> <span class="token string">&quot;xhl&quot;</span><span class="token punctuation">,</span>
    <span class="token property">&quot;title&quot;</span><span class="token operator">:</span> <span class="token string">&quot;手把手教你spring boot&quot;</span><span class="token punctuation">,</span>
    <span class="token property">&quot;content&quot;</span><span class="token operator">:</span> <span class="token string">&quot;hello world&quot;</span><span class="token punctuation">,</span>
    <span class="token property">&quot;createTime&quot;</span><span class="token operator">:</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">,</span>
    <span class="token property">&quot;reader&quot;</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">{</span><span class="token property">&quot;name&quot;</span><span class="token operator">:</span><span class="token string">&quot;xhl&quot;</span><span class="token punctuation">,</span><span class="token property">&quot;age&quot;</span><span class="token operator">:</span><span class="token number">18</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">&quot;name&quot;</span><span class="token operator">:</span><span class="token string">&quot;jng&quot;</span><span class="token punctuation">,</span><span class="token property">&quot;age&quot;</span><span class="token operator">:</span><span class="token number">19</span><span class="token punctuation">}</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>下面以测试新增文章的接口为例：</p> <ul><li>测试的接口服务端点为“/rest/article”</li> <li>服务端点支持的HTTP方法为POST</li> <li>使用Http协议的body传输JSON数据，对应Controller应该使用@RequestBody进行数据参数接收</li> <li>点击Send进行接口数据的发送</li></ul> <h2 id="bug"><a href="#bug" class="header-anchor">#</a> bug</h2> <p><img alt="image-20231104190819684" data-src="https://pic.yupi.icu/5563/image-20231104190819684.png" loading="lazy" class="lazy"></p> <p>既然和 DispatcherServlet 有关，那无非就是MVC的映射出了问题，通俗的理解，就是：JVM编译期有个 <a href="https://so.csdn.net/so/search?q=servlet&amp;spm=1001.2101.3001.7020" target="_blank" rel="noopener noreferrer">servlet<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 加载/调用失败了。</p> <p>我画了一张图，和大家一起复习下 DispatcherServlet ：</p> <p><img alt="image-20231104190832277" data-src="https://pic.yupi.icu/5563/image-20231104190832277.png" loading="lazy" class="lazy"></p> <p>整个流程可以被大致描述为：</p> <ol><li>一个http请求到达服务器，被DispatcherServlet接收。</li> <li>DispatcherServlet将请求委派给合适的处理器Controller，此时处理控制权到达Controller对象。</li> <li>Controller内部完成请求的数据模型的创建和业务逻辑的处理，然后再将填充了数据后的模型即model和控制权一并交还给DispatcherServlet，委派DispatcherServlet来渲染响应。</li> <li>DispatcherServlet再将这些数据和适当的数据模版视图结合，向Response输出响应。</li></ol> <p>解决：在实体类加上两个注解@AllArgsConstructor 和 @NoArgsConstructor</p> <p><img alt="image-20231104190853349" data-src="https://pic.yupi.icu/5563/image-20231104190853349.png" loading="lazy" class="lazy"></p> <p><img alt="image-20231104190903249" data-src="https://pic.yupi.icu/5563/image-20231104190903249.png" loading="lazy" class="lazy"></p> <h1 id="jackson全局配置"><a href="#jackson全局配置" class="header-anchor">#</a> Jackson全局配置</h1> <p>在Spring框架内使用Jackson的时候，通常需要一些特殊的全局配置，来应对我们JSON序列化与反序列化中出现的各种问题。
Spring Boot 提供了两种配置方式，一是配置文件的方式</p> <div class="language-YAML line-numbers-mode"><pre class="language-yaml"><code><span class="token key atrule">spring</span><span class="token punctuation">:</span>
  <span class="token key atrule">jackson</span><span class="token punctuation">:</span>
    <span class="token comment">#日期类型格式化</span>
    <span class="token key atrule">date-format</span><span class="token punctuation">:</span> yyyy<span class="token punctuation">-</span>MM<span class="token punctuation">-</span>dd HH<span class="token punctuation">:</span>mm<span class="token punctuation">:</span>ss
    <span class="token key atrule">serialization</span><span class="token punctuation">:</span>
      <span class="token comment">#格式化输出，通常为了节省网络流量设置为false。因为格式化之后会带有缩进，方便阅读。</span>
      <span class="token key atrule">indent_output</span><span class="token punctuation">:</span> <span class="token boolean important">false</span>
      <span class="token comment">#某些类对象无法序列化的时候，是否报错</span>
      <span class="token key atrule">fail_on_empty_beans</span><span class="token punctuation">:</span> <span class="token boolean important">false</span>
    <span class="token comment">#设置空如何序列化，见下文代码方式详解</span>
    <span class="token key atrule">defaultPropertyInclusion</span><span class="token punctuation">:</span> NON_EMPTY
    <span class="token key atrule">deserialization</span><span class="token punctuation">:</span>
      <span class="token comment">#json对象中有不存在的属性时候，是否报错</span>
      <span class="token key atrule">fail_on_unknown_properties</span><span class="token punctuation">:</span> <span class="token boolean important">false</span>
    <span class="token key atrule">parser</span><span class="token punctuation">:</span>
      <span class="token comment">#允许出现特殊字符和转义符</span>
      <span class="token key atrule">allow_unquoted_control_chars</span><span class="token punctuation">:</span> <span class="token boolean important">true</span>
      <span class="token comment">#允许出现单引号</span>
      <span class="token key atrule">allow_single_quotes</span><span class="token punctuation">:</span> <span class="token boolean important">true</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br></div></div><p>二是通过代码的方式，方式一更容易，方式二更灵活。方式一无法解决的问题，尝试使用方式二。</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Bean</span>
<span class="token annotation punctuation">@Primary</span>
<span class="token annotation punctuation">@ConditionalOnMissingBean</span><span class="token punctuation">(</span><span class="token class-name">ObjectMapper</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token class-name">ObjectMapper</span> <span class="token function">jacksonObjectMapper</span><span class="token punctuation">(</span><span class="token class-name">Jackson2ObjectMapperBuilder</span> builder<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
        <span class="token class-name">ObjectMapper</span> objectMapper <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">createXmlMapper</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token comment">// 通过该方法对mapper对象进行设置，所有序列化的对象都将按改规则进行系列化</span>
        <span class="token comment">// Include.Include.ALWAYS 默认</span>
        <span class="token comment">// Include.NON_DEFAULT 属性为默认值不序列化</span>
        <span class="token comment">// Include.NON_EMPTY 属性为 空（&quot;&quot;） 或者为 NULL 都不序列化，则返回的json是没有这个字段的。这样对移动端会更省流量</span>
        <span class="token comment">// Include.NON_NULL 属性为NULL 不序列化</span>
        objectMapper<span class="token punctuation">.</span><span class="token function">setSerializationInclusion</span><span class="token punctuation">(</span><span class="token class-name">JsonInclude<span class="token punctuation">.</span>Include</span><span class="token punctuation">.</span><span class="token constant">NON_EMPTY</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        objectMapper<span class="token punctuation">.</span><span class="token function">configure</span><span class="token punctuation">(</span><span class="token class-name">DeserializationFeature</span><span class="token punctuation">.</span><span class="token constant">FAIL_ON_UNKNOWN_PROPERTIES</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 允许出现特殊字符和转义符</span>
        objectMapper<span class="token punctuation">.</span><span class="token function">configure</span><span class="token punctuation">(</span><span class="token class-name">JsonParser<span class="token punctuation">.</span>Feature</span><span class="token punctuation">.</span><span class="token constant">ALLOW_UNQUOTED_CONTROL_CHARS</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 允许出现单引号</span>
        objectMapper<span class="token punctuation">.</span><span class="token function">configure</span><span class="token punctuation">(</span><span class="token class-name">JsonParser<span class="token punctuation">.</span>Feature</span><span class="token punctuation">.</span><span class="token constant">ALLOW_SINGLE_QUOTES</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 字段保留，将null值转为&quot;&quot;</span>
        objectMapper<span class="token punctuation">.</span><span class="token function">getSerializerProvider</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setNullValueSerializer</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">JsonSerializer</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Object</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token annotation punctuation">@Override</span>
            <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">serialize</span><span class="token punctuation">(</span><span class="token class-name">Object</span> o<span class="token punctuation">,</span> <span class="token class-name">JsonGenerator</span> jsonGenerator<span class="token punctuation">,</span>
                                  <span class="token class-name">SerializerProvider</span> serializerProvider<span class="token punctuation">)</span>
                    <span class="token keyword">throws</span> <span class="token class-name">IOException</span>
            <span class="token punctuation">{</span>
                jsonGenerator<span class="token punctuation">.</span><span class="token function">writeString</span><span class="token punctuation">(</span><span class="token string">&quot;&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> objectMapper<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br></div></div><h1 id="编码实现接口测试"><a href="#编码实现接口测试" class="header-anchor">#</a> 编码实现接口测试</h1> <p>为什么要写代码做测试？</p> <p>使用maven在打包之前将所有的测试用例执行一遍。这里重点是<strong>自动化</strong>，所以postman这种工具很难插入到持续集成的自动化流程中去。</p> <h2 id="junit测试框架"><a href="#junit测试框架" class="header-anchor">#</a> junit测试框架</h2> <p>在junit4和junit5中，注解的写法有些许变化。</p> <h2 id="mockito测试框架"><a href="#mockito测试框架" class="header-anchor">#</a> Mockito测试框架</h2> <p>Mockito是GitHub上使用最广泛的Mock框架,并与JUnit结合使用.Mockito框架可以创建和配置mock对象.使用Mockito简化了具有外部依赖的类的测试开发。Mockito测试框架可以帮助我们模拟HTTP请求，从而达到在服务端测试目的。<strong>因为其不会真的去发送HTTP请求，而是模拟HTTP请求内容，从而节省了HTTP请求的网络传输，测试速度更快。</strong></p> <p><img alt="image-20231104190922436" data-src="https://pic.yupi.icu/5563/image-20231104190922436.png" loading="lazy" class="lazy"></p> <blockquote><p>spring-boot-starter-test(Spring Boot 2.3.0.RELEASE)自动包含Junit 5 和Mockito框架</p></blockquote> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Slf4j</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ArticleRestControllerTest</span> <span class="token punctuation">{</span>

    <span class="token comment">//mock对象</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">MockMvc</span> mockMvc<span class="token punctuation">;</span>

    <span class="token comment">//在所有测试方法执行之前进行mock对象初始化</span>
    <span class="token annotation punctuation">@BeforeAll</span>
    <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">setUp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        mockMvc <span class="token operator">=</span> <span class="token class-name">MockMvcBuilders</span><span class="token punctuation">.</span><span class="token function">standaloneSetup</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ArticleController</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">//测试方法</span>
    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">saveArticle</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span>

        <span class="token class-name">String</span> article <span class="token operator">=</span> <span class="token string">&quot;{\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;id\&quot;: 1,\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;author\&quot;: \&quot;xhl\&quot;,\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;title\&quot;: \&quot;手把手教你开发spring boot\&quot;,\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;content\&quot;: \&quot;c\&quot;,\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;createTime\&quot;: \&quot;2023-11-03 15:56:55\&quot;,\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;reader\&quot;:[{\&quot;name\&quot;:\&quot;xhl\&quot;,\&quot;age\&quot;:18},{\&quot;name\&quot;:\&quot;jng\&quot;,\&quot;age\&quot;:19}]\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;}&quot;</span><span class="token punctuation">;</span>
        <span class="token class-name">MvcResult</span> result <span class="token operator">=</span> mockMvc<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span>
            <span class="token class-name">MockMvcRequestBuilders</span>
                <span class="token punctuation">.</span><span class="token function">request</span><span class="token punctuation">(</span><span class="token class-name">HttpMethod</span><span class="token punctuation">.</span><span class="token constant">POST</span><span class="token punctuation">,</span> <span class="token string">&quot;/rest/article&quot;</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">contentType</span><span class="token punctuation">(</span><span class="token string">&quot;application/json&quot;</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">content</span><span class="token punctuation">(</span>article<span class="token punctuation">)</span>
        <span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token class-name">MockMvcResultMatchers</span><span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isOk</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment">//HTTP:status 200</span>
        <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token class-name">MockMvcResultMatchers</span><span class="token punctuation">.</span><span class="token function">jsonPath</span><span class="token punctuation">(</span><span class="token string">&quot;$.data.author&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token string">&quot;xhl&quot;</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token class-name">MockMvcResultMatchers</span><span class="token punctuation">.</span><span class="token function">jsonPath</span><span class="token punctuation">(</span><span class="token string">&quot;$.data.reader[0].age&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token number">18</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">andDo</span><span class="token punctuation">(</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">andReturn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        result<span class="token punctuation">.</span><span class="token function">getResponse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setCharacterEncoding</span><span class="token punctuation">(</span><span class="token string">&quot;UTF-8&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span><span class="token function">getResponse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getContentAsString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br></div></div><p>MockMvc对象有以下几个基本的方法:</p> <ul><li>perform : 模拟执行一个RequestBuilder构建的HTTP请求，会执行SpringMVC的流程并映射到相应的控制器Controller执行。</li> <li>contentType：发送请求内容的序列化的格式，&quot;application/json&quot;表示JSON数据格式</li> <li>andExpect: 添加RequsetMatcher验证规则，验证控制器执行完成后结果是否正确，或者说是结果是否与我们期望（Expect）的一致。</li> <li>andDo: 添加ResultHandler结果处理器，比如调试时打印结果到控制台</li> <li>andReturn: 最后返回相应的MvcResult,然后进行自定义验证/进行下一步的异步处理</li></ul> <blockquote><p>上面的整个过程，我们都没有使用到Spring Context依赖注入、也没有启动tomcat web容器。整个测试的过程十分的轻量级，速度很快。</p></blockquote> <h2 id="真实servlet容器环境下的测试"><a href="#真实servlet容器环境下的测试" class="header-anchor">#</a> 真实servlet容器环境下的测试</h2> <p>上面的测试执行速度非常快，但是有一个问题：它没有启动servlet容器和Spring 上下文，自然也就无法实现依赖注入（不支持@Resource和@AutoWired注解）。这就导致它在从控制层到持久层全流程测试中有很大的局限性。</p> <p>测试类上面额外加上这样两个注解，并且mockMvc对象使用@Resource自动注入，删掉Before注解及setUp函数。</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@AutoConfigureMockMvc</span>
<span class="token annotation punctuation">@SpringBootTest</span>
<span class="token annotation punctuation">@ExtendWith</span><span class="token punctuation">(</span><span class="token class-name">SpringExtension</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p><img alt="image-20231104191101956" data-src="https://pic.yupi.icu/5563/image-20231104191101956.png" loading="lazy" class="lazy"></p> <p>该测试方法真实的启动了一个tomcat容器、以及Spring 上下文，所以我们可以进行依赖注入（@Resource）。实现的效果和使用MockMvcBuilders构建MockMVC对象的效果是一样的，但是有一个非常明显的缺点：<strong>每次做一个接口测试，都会真实的启动一次servlet容器，Spring上下文加载项目里面定义的所有的Bean，导致执行过程很缓慢。</strong></p> <h3 id="springboottest-注解"><a href="#springboottest-注解" class="header-anchor">#</a> @SpringBootTest 注解</h3> <p>是用来创建Spring的上下文ApplicationContext，保证测试在上下文环境里运行。单独使用@SpringBootTest不会启动servlet容器。所以<strong>只是使用SpringBootTest 注解，不可以使用@Resource和@Autowired等注解进行bean的依赖注入</strong>。</p> <h3 id="extendwith-runwith注解"><a href="#extendwith-runwith注解" class="header-anchor">#</a> @ExtendWith(@RunWith注解)</h3> <ul><li>RunWith方法为我们构造了一个的Servlet容器运行运行环境，并在此环境下测试。然而为什么要构建servlet容器？因为使用了依赖注入，注入了MockMvc对象，而在上一个例子里面是我们自己new的。</li> <li>而@AutoConfigureMockMvc注解，该注解表示mockMvc对象由spring 依赖注入构建，你只负责使用就可以了。这种写法是为了让测试在servlet容器环境下执行。</li></ul> <p>实际上@SpringBootTest 注解注解已经包含了 @ExtendWith注解，如果使用了前者，可以忽略后者！</p> <h3 id="transactional"><a href="#transactional" class="header-anchor">#</a> @Transactional</h3> <p>该注解加在方法上可以使单元测试进行事务回滚，以保证数据库表中没有因测试造成的垃圾数据，因此保证单元测试可以反复执行；但是使用该注解会破坏测试真实性。</p> <h2 id="mock测试"><a href="#mock测试" class="header-anchor">#</a> Mock测试</h2> <h3 id="什么是mock"><a href="#什么是mock" class="header-anchor">#</a> 什么是Mock?</h3> <p>在面向对象程序设计中，模拟对象是以可控的方式模拟真实对象行为的<strong>假的对象</strong></p> <blockquote><p>在单元测试中，模拟对象可以模拟复杂的、真实的对象的行为， 如果真实的对象无法放入单元测试中，使用模拟对象就很有帮助。</p></blockquote> <p>在下面的情形，可能需要使用 <strong>&quot;模拟对象行为&quot;</strong> 来代替真实对象：</p> <ul><li>真实对象的行为是不确定的（例如，当前的时间或当前的温度）；</li> <li>真实对象很难搭建起来；</li> <li>真实对象的行为很难触发（例如，网络错误）；</li> <li>真实对象速度很慢（例如，一个完整的数据库，在测试之前可能需要初始化）；</li> <li>真实的对象是用户界面，或包括用户界面在内；</li> <li>真实的对象使用了回调机制；</li> <li>真实对象可能还不存在（例如，其他程序员还为完成工作）；</li> <li>真实对象可能包含不能用作测试的信息（高度保密信息等）和方法。</li></ul> <h3 id="场景实践"><a href="#场景实践" class="header-anchor">#</a> 场景实践</h3> <p>我们的保存文章的Controller方法，调用ArticleService的saveArticle进行文章的保存。</p> <p>但是因为种种原因，这个接口目前没能实现(只有接口）</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">ArticleService</span> <span class="token punctuation">{</span>
  <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">saveArticle</span><span class="token punctuation">(</span><span class="token class-name">Article</span> article<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>我们就可以使用Mock的方法，先Mock一个假的ArticleService，把接口验证完成。</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Slf4j</span>
<span class="token annotation punctuation">@AutoConfigureMockMvc</span>
<span class="token annotation punctuation">@SpringBootTest</span>
<span class="token comment">//@ExtendWith(SpringExtension.class)</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ArticleRestControllerTest</span> <span class="token punctuation">{</span>

<span class="token comment">//    //mock对象</span>
<span class="token comment">//    private static MockMvc mockMvc;</span>

    <span class="token annotation punctuation">@Resource</span>
    <span class="token keyword">private</span> <span class="token class-name">MockMvc</span> mockMvc<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@MockBean</span>
    <span class="token keyword">private</span> <span class="token class-name">ArticleService</span> articleService<span class="token punctuation">;</span>

<span class="token comment">//    //在所有测试方法执行之前进行mock对象初始化</span>
<span class="token comment">//    @BeforeAll</span>
<span class="token comment">//    static void setUp() {</span>
<span class="token comment">//        mockMvc = MockMvcBuilders.standaloneSetup(new ArticleController()).build();</span>
<span class="token comment">//    }</span>

    <span class="token comment">//测试方法</span>
    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">saveArticle</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span>

        <span class="token class-name">String</span> article <span class="token operator">=</span> <span class="token string">&quot;{\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;id\&quot;: 1,\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;author\&quot;: \&quot;xhl\&quot;,\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;title\&quot;: \&quot;手把手教你开发spring boot\&quot;,\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;content\&quot;: \&quot;c\&quot;,\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;createTime\&quot;: \&quot;2023-11-03 15:56:55\&quot;,\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;    \&quot;reader\&quot;:[{\&quot;name\&quot;:\&quot;xhl\&quot;,\&quot;age\&quot;:18},{\&quot;name\&quot;:\&quot;jng\&quot;,\&quot;age\&quot;:19}]\n&quot;</span> <span class="token operator">+</span>
                <span class="token string">&quot;}&quot;</span><span class="token punctuation">;</span>

        <span class="token comment">//反序列化</span>
        <span class="token class-name">ObjectMapper</span> objectMapper <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ObjectMapper</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">Article</span> articleObj <span class="token operator">=</span> objectMapper<span class="token punctuation">.</span><span class="token function">readValue</span><span class="token punctuation">(</span>article<span class="token punctuation">,</span> <span class="token class-name">Article</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token comment">//打桩</span>
        <span class="token function">when</span><span class="token punctuation">(</span>articleService<span class="token punctuation">.</span><span class="token function">saveArticle</span><span class="token punctuation">(</span>articleObj<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenReturn</span><span class="token punctuation">(</span><span class="token string">&quot;ok&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>


        <span class="token class-name">MvcResult</span> result <span class="token operator">=</span> mockMvc<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span>
                        <span class="token class-name">MockMvcRequestBuilders</span>
                                <span class="token punctuation">.</span><span class="token function">request</span><span class="token punctuation">(</span><span class="token class-name">HttpMethod</span><span class="token punctuation">.</span><span class="token constant">POST</span><span class="token punctuation">,</span> <span class="token string">&quot;/rest/article&quot;</span><span class="token punctuation">)</span>
                                <span class="token punctuation">.</span><span class="token function">contentType</span><span class="token punctuation">(</span><span class="token string">&quot;application/json&quot;</span><span class="token punctuation">)</span>
                                <span class="token punctuation">.</span><span class="token function">content</span><span class="token punctuation">(</span>article<span class="token punctuation">)</span>
                <span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token class-name">MockMvcResultMatchers</span><span class="token punctuation">.</span><span class="token function">jsonPath</span><span class="token punctuation">(</span><span class="token string">&quot;$.data&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token string">&quot;ok&quot;</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token comment">//        .andExpect(MockMvcResultMatchers.status().isOk())  //HTTP:status 200</span>
<span class="token comment">//        .andExpect(MockMvcResultMatchers.jsonPath(&quot;$.data.author&quot;).value(&quot;xhl&quot;))</span>
<span class="token comment">//        .andExpect(MockMvcResultMatchers.jsonPath(&quot;$.data.reader[0].age&quot;).value(18))</span>
                <span class="token punctuation">.</span><span class="token function">andDo</span><span class="token punctuation">(</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">andReturn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        result<span class="token punctuation">.</span><span class="token function">getResponse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setCharacterEncoding</span><span class="token punctuation">(</span><span class="token string">&quot;UTF-8&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span><span class="token function">getResponse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getContentAsString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br></div></div><h3 id="mockbean"><a href="#mockbean" class="header-anchor">#</a> @MockBean</h3> <p>可以用MockBean伪造模拟一个Service ，如代码中的MockBean。</p> <p>大家注意上文代码中，打了一个桩</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token function">when</span><span class="token punctuation">(</span>articleService<span class="token punctuation">.</span><span class="token function">saveArticle</span><span class="token punctuation">(</span>articleObj<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenReturn</span><span class="token punctuation">(</span><span class="token string">&quot;ok&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>也就是告诉测试用例程序，当你调用articleService.saveArticle(articleObj)方法的时候，不要去真的调用这个方法，直接返回一个结果（“ok”）就好了。</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token class-name">MockMvcResultMatchers</span><span class="token punctuation">.</span><span class="token function">jsonPath</span><span class="token punctuation">(</span><span class="token string">&quot;$.data&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token string">&quot;ok&quot;</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>测试用例跑通了，期望结果andExpect：ok与实际结果thenReturn(&quot;ok&quot;)一致。表示程序真正的去执行了MockBean的模拟行为，而不是调用真实对象的方法。</p> <p>注意这里要在Controller层调用service方法</p> <p><img alt="image-20231104191125588" data-src="https://pic.yupi.icu/5563/image-20231104191125588.png" loading="lazy" class="lazy"></p> <p><img alt="image-20231104191133795" data-src="https://pic.yupi.icu/5563/image-20231104191133795.png" loading="lazy" class="lazy"></p> <h2 id="轻量级测试"><a href="#轻量级测试" class="header-anchor">#</a> 轻量级测试</h2> <p>在ExtendWith的AutoConfigureMockMvc注解的共同作用下，启动了SpringMVC的运行容器，并且把项目中所有的@Bean全部都注入进来。把所有的bean都注入进来是不是很臃肿？这样会拖慢单元测试的效率。如果我只是想测试一下控制层Controller，怎么办？</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@ExtendWith</span><span class="token punctuation">(</span><span class="token class-name">SpringExtension</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token annotation punctuation">@WebMvcTest</span><span class="token punctuation">(</span><span class="token class-name">ArticleController</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token comment">//@SpringBootTest</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><h4 id="使用-webmvctest替换-springboottest"><a href="#使用-webmvctest替换-springboottest" class="header-anchor">#</a> 使用@WebMvcTest替换@SpringBootTest</h4> <ul><li>@SpringBootTest注解告诉SpringBoot去寻找一个主配置类(例如带有@SpringBootApplication的配置类)，并使用它来启动Spring应用程序上下文。SpringBootTest加载完整的应用程序并注入所有可能的bean，因此速度会很慢。</li> <li>@WebMvcTest注解主要用于controller层测试，只覆盖应用程序的controller层，@WebMvcTest(ArticleController.class)只加载ArticleController这一个Bean用作测试。所以WebMvcTest要快得多，因为我们只加载了应用程序的一小部分。</li></ul> <h2 id="mockmvc更多的用法总结"><a href="#mockmvc更多的用法总结" class="header-anchor">#</a> MockMvc更多的用法总结</h2> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token comment">//模拟GET请求：</span>
mockMvc<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token class-name">MockMvcRequestBuilders</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&quot;/user/{id}&quot;</span><span class="token punctuation">,</span> userId<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">//模拟Post请求：</span>
mockMvc<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token class-name">MockMvcRequestBuilders</span><span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">&quot;uri&quot;</span><span class="token punctuation">,</span> parameters<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">//模拟文件上传：</span>
mockMvc<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token class-name">MockMvcRequestBuilders</span><span class="token punctuation">.</span><span class="token function">multipart</span><span class="token punctuation">(</span><span class="token string">&quot;uri&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">file</span><span class="token punctuation">(</span><span class="token string">&quot;fileName&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;file&quot;</span><span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token string">&quot;UTF-8&quot;</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>


<span class="token comment">//模拟session和cookie：</span>
mockMvc<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token class-name">MockMvcRequestBuilders</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&quot;uri&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">sessionAttr</span><span class="token punctuation">(</span><span class="token string">&quot;name&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;value&quot;</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
mockMvc<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token class-name">MockMvcRequestBuilders</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&quot;uri&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">cookie</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Cookie</span><span class="token punctuation">(</span><span class="token string">&quot;name&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;value&quot;</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">//设置HTTP Header：</span>
mockMvc<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token class-name">MockMvcRequestBuilders</span>
                        <span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&quot;uri&quot;</span><span class="token punctuation">,</span> parameters<span class="token punctuation">)</span>
                        <span class="token punctuation">.</span><span class="token function">contentType</span><span class="token punctuation">(</span><span class="token string">&quot;application/x-www-form-urlencoded&quot;</span><span class="token punctuation">)</span>
                        <span class="token punctuation">.</span><span class="token function">accept</span><span class="token punctuation">(</span><span class="token string">&quot;application/json&quot;</span><span class="token punctuation">)</span>
                        <span class="token punctuation">.</span><span class="token function">header</span><span class="token punctuation">(</span><span class="token string">&quot;&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br></div></div><h1 id="使用swagger2构建api文档"><a href="#使用swagger2构建api文档" class="header-anchor">#</a> 使用Swagger2构建API文档</h1> <h2 id="为什么需要api接口文档"><a href="#为什么需要api接口文档" class="header-anchor">#</a> 为什么需要API接口文档?</h2> <p>当下很多公司都采取前后端分离的开发模式，前端和后端的工作由不同的工程师完成。在这种开发模式下，维护一份及时更新且完整的API 文档将会极大的提高我们的工作效率。传统意义上的文档都是后端开发人员使用word编写的，相信大家也都知道这种方式很难保证文档的及时性，这种文档久而久之也就会失去其参考意义，反而还会加大我们的沟通成本。而 Swagger 给我们提供了一个全新的维护 API 文档的方式，下面我们就来了解一下它的优点：</p> <ul><li>代码变，文档变。只需要少量的注解，Swagger 就可以根据代码自动生成 API 文档，很好的保证了文档的时效性。</li> <li>跨语言性，支持 40 多种语言。</li> <li>Swagger UI 呈现出来的是一份可交互式的 API 文档，我们可以直接在文档页面尝试 API 的调用，省去了准备复杂的调用参数的过程。</li> <li>还可以将文档规范导入相关的工具（例如 SoapUI）, 这些工具将会为我们自动地创建自动化测试。</li></ul> <h2 id="整合swagger2生成文档"><a href="#整合swagger2生成文档" class="header-anchor">#</a> 整合swagger2生成文档</h2> <ol><li>引入依赖</li></ol> <div class="language-XML line-numbers-mode"><pre class="language-xml"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">&gt;</span></span>io.springfox<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">&gt;</span></span>springfox-swagger2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">&gt;</span></span>2.6.1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">&gt;</span></span>io.springfox<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">&gt;</span></span>springfox-swagger-ui<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">&gt;</span></span>2.6.1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">&gt;</span></span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><ol><li>Config配置</li></ol> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Configuration</span>
<span class="token annotation punctuation">@EnableSwagger2</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Swagger2</span><span class="token punctuation">{</span>

    <span class="token keyword">private</span> <span class="token class-name">ApiInfo</span> <span class="token function">apiInfo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ApiInfoBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">title</span><span class="token punctuation">(</span><span class="token string">&quot;springboot利用swagger构建api文档&quot;</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">description</span><span class="token punctuation">(</span><span class="token string">&quot;简单优雅的restfun风格&quot;</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">termsOfServiceUrl</span><span class="token punctuation">(</span><span class="token string">&quot;https://blog.csdn.net/m0_60496161?spm=1010.2135.3001.5343&quot;</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">version</span><span class="token punctuation">(</span><span class="token string">&quot;1.0&quot;</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Bean</span>
    <span class="token keyword">public</span> <span class="token class-name">Docket</span> <span class="token function">createRestApi</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Docket</span><span class="token punctuation">(</span><span class="token class-name">DocumentationType</span><span class="token punctuation">.</span><span class="token constant">SWAGGER_2</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">apiInfo</span><span class="token punctuation">(</span><span class="token function">apiInfo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">select</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                <span class="token comment">//扫描basePackage包下面的“/rest/”路径下的内容作为接口文档构建的目标</span>
                <span class="token punctuation">.</span><span class="token function">apis</span><span class="token punctuation">(</span><span class="token class-name">RequestHandlerSelectors</span><span class="token punctuation">.</span><span class="token function">basePackage</span><span class="token punctuation">(</span><span class="token string">&quot;com.xhl.firstdemo.Controller&quot;</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">paths</span><span class="token punctuation">(</span><span class="token class-name">PathSelectors</span><span class="token punctuation">.</span><span class="token function">any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>




<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br></div></div><ul><li>@EnableSwagger2 注解表示开启SwaggerAPI文档相关的功能</li> <li>在apiInfo方法中配置接口文档的title(标题)、描述、termsOfServiceUrl（服务协议）、版本等相关信息</li> <li>在createRestApi方法中，<strong>basePackage表示扫描哪个package下面的Controller类作为API接口文档内容范围</strong></li> <li>在createRestApi方法中，paths表示哪一个请求路径下控制器映射方法，作为API接口文档内容范围</li></ul> <h3 id="bug-2"><a href="#bug-2" class="header-anchor">#</a> bug</h3> <ol><li>Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException</li></ol> <p>解决办法：在启动类加一个注解：<code>@EnableWebMvc</code></p> <ol><li>mvc报错</li></ol> <p><img alt="image-20231104191243003" data-src="https://pic.yupi.icu/5563/image-20231104191243003.png" loading="lazy" class="lazy"></p> <p>解决方法：https://blog.csdn.net/qq_39508627/article/details/104490268</p> <p>添加一个配置类</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Configuration</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">WebMvcConfigurer</span> <span class="token keyword">extends</span> <span class="token class-name">WebMvcConfigurationSupport</span> <span class="token punctuation">{</span>

    <span class="token comment">/**
     * 发现如果继承了WebMvcConfigurationSupport，则在yml中配置的相关内容会失效。 需要重新指定静态资源
     *
     * @param registry
     */</span>
    <span class="token annotation punctuation">@Override</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">addResourceHandlers</span><span class="token punctuation">(</span><span class="token class-name">ResourceHandlerRegistry</span> registry<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        registry<span class="token punctuation">.</span><span class="token function">addResourceHandler</span><span class="token punctuation">(</span><span class="token string">&quot;/**&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addResourceLocations</span><span class="token punctuation">(</span>
                <span class="token string">&quot;classpath:/static/&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        registry<span class="token punctuation">.</span><span class="token function">addResourceHandler</span><span class="token punctuation">(</span><span class="token string">&quot;swagger-ui.html&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addResourceLocations</span><span class="token punctuation">(</span>
                <span class="token string">&quot;classpath:/META-INF/resources/&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        registry<span class="token punctuation">.</span><span class="token function">addResourceHandler</span><span class="token punctuation">(</span><span class="token string">&quot;/webjars/**&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addResourceLocations</span><span class="token punctuation">(</span>
                <span class="token string">&quot;classpath:/META-INF/resources/webjars/&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">addResourceHandlers</span><span class="token punctuation">(</span>registry<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br></div></div><p><img alt="image-20231104191252240" data-src="https://pic.yupi.icu/5563/image-20231104191252240.png" loading="lazy" class="lazy"></p> <p>swagger不仅提供了静态的接口文档的展示，还提供了执行接口方法测试的功能。在下图中填入接口对应的参数，点击“try it out&quot;就可以实现接口请求的发送与响应结果的展示。</p> <p><img alt="image-20231104191300977" data-src="https://pic.yupi.icu/5563/image-20231104191300977.png" loading="lazy" class="lazy"></p> <ol><li><p>Consider defining a bean of type 'com.xhl.firstdemo.service.ArticleService' in your configuration.</p> <p>https://cloud.tencent.com/developer/article/2124710</p> <p>既然他说没找到，也就是该注解被没有被spring识别</p> <p>在Spring框架中，<code>@Service</code>和<code>@Component</code>注解都可以用于类级别的注解，用来标识一个类是Spring的组件（component）。这些注解可以让Spring自动扫描并将这些类实例化为可用的bean，并将它们注册到应用程序的上下文中。</p> <p>具体来说，<code>@Component</code>是一个泛化的注解，可以用于任何类型的组件。而<code>@Service</code>注解则是<code>@Component</code>的一个特化版本，它用于表示业务逻辑层（Service层）的组件。</p> <p>解决：</p> <ol><li>将接口与对应的实现类放在与application启动类的同一个目录或者他的子目录下，这样注解可以被扫描到</li> <li>在指定的application类上加上这么一行注解，手动指定application类要扫描哪些包下的注解</li></ol></li></ol> <h2 id="书写swagger注解"><a href="#书写swagger注解" class="header-anchor">#</a> 书写swagger注解</h2> <p>为接口功能添加注释</p> <div class="language-Java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@ApiOperation</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token string">&quot;添加文章&quot;</span><span class="token punctuation">,</span> notes <span class="token operator">=</span> <span class="token string">&quot;添加新的文章&quot;</span><span class="token punctuation">,</span> tags <span class="token operator">=</span> <span class="token string">&quot;Article&quot;</span><span class="token punctuation">,</span>httpMethod <span class="token operator">=</span> <span class="token string">&quot;POST&quot;</span><span class="token punctuation">)</span>
<span class="token annotation punctuation">@ApiImplicitParams</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        <span class="token annotation punctuation">@ApiImplicitParam</span><span class="token punctuation">(</span>name <span class="token operator">=</span> <span class="token string">&quot;title&quot;</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token string">&quot;文章标题&quot;</span><span class="token punctuation">,</span> required <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">,</span> dataType <span class="token operator">=</span> <span class="token string">&quot;String&quot;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token annotation punctuation">@ApiImplicitParam</span><span class="token punctuation">(</span>name <span class="token operator">=</span> <span class="token string">&quot;content&quot;</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token string">&quot;文章内容&quot;</span><span class="token punctuation">,</span> required <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">,</span> dataType <span class="token operator">=</span> <span class="token string">&quot;String&quot;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token annotation punctuation">@ApiImplicitParam</span><span class="token punctuation">(</span>name <span class="token operator">=</span> <span class="token string">&quot;author&quot;</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token string">&quot;文章作者&quot;</span><span class="token punctuation">,</span> required <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">,</span> dataType <span class="token operator">=</span> <span class="token string">&quot;String&quot;</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token annotation punctuation">@ApiResponses</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        <span class="token annotation punctuation">@ApiResponse</span><span class="token punctuation">(</span>code<span class="token operator">=</span><span class="token number">200</span><span class="token punctuation">,</span>message<span class="token operator">=</span><span class="token string">&quot;成功&quot;</span><span class="token punctuation">,</span>response<span class="token operator">=</span><span class="token class-name">AjaxResponse</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/article&quot;</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token annotation punctuation">@ResponseBody</span>  <span class="token class-name">AjaxResponse</span> <span class="token function">saveArticle</span><span class="token punctuation">(</span>
        <span class="token annotation punctuation">@RequestParam</span><span class="token punctuation">(</span>value<span class="token operator">=</span><span class="token string">&quot;title&quot;</span><span class="token punctuation">)</span> <span class="token class-name">String</span> title<span class="token punctuation">,</span>  <span class="token comment">//参数1</span>
        <span class="token annotation punctuation">@RequestParam</span><span class="token punctuation">(</span>value<span class="token operator">=</span><span class="token string">&quot;content&quot;</span><span class="token punctuation">)</span> <span class="token class-name">String</span> content<span class="token punctuation">,</span><span class="token comment">//参数2</span>
        <span class="token annotation punctuation">@RequestParam</span><span class="token punctuation">(</span>value<span class="token operator">=</span><span class="token string">&quot;author&quot;</span><span class="token punctuation">)</span> <span class="token class-name">String</span> author<span class="token punctuation">,</span><span class="token comment">//参数3</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><p><img alt="image-20231104191312060" data-src="https://pic.yupi.icu/5563/image-20231104191312060.png" loading="lazy" class="lazy"></p> <h2 id="生产环境下如何禁用swagger2"><a href="#生产环境下如何禁用swagger2" class="header-anchor">#</a> 生产环境下如何禁用swagger2</h2> <p>使用注解@Profile({&quot;dev&quot;,&quot;test&quot;}) 表示在开发或测试环境开启，而在生产关闭。</p> <div class="language-YAML line-numbers-mode"><pre class="language-yaml"><code><span class="token key atrule">spring</span><span class="token punctuation">:</span>
  <span class="token key atrule">profiles</span><span class="token punctuation">:</span>
    actice<span class="token punctuation">:</span>dev
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div></div> <footer class="page-edit" style="margin:0;"><div class="edit-link"><a href="https://github.com/liyupi/codefather/edit/master/知识碎片/RESTful 接口实现与测试( Spring Boot ).md" target="_blank" rel="noopener noreferrer">完善页面</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <div class="last-updated"><span class="prefix">最近更新:</span> <span class="time">11/11/2023, 8:17:47 PM</span></div></footer> <!----></div> <div class="toc-container-sidebar"><div class="pos-box"><div class="icon-arrow"></div> <div class="scroll-box" style="max-height:86vh"><div style="font-weight:bold;">RESTful 接口实现与测试( Spring Boot )</div> <hr> <div class="toc-box"><ul class="toc-sidebar-links"><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#一、定义pojo对象" class="toc-sidebar-link">一、定义pojo对象</a><ul class="toc-sidebar-sub-headers"></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#二、定义http方法和controller" class="toc-sidebar-link">二、定义HTTP方法和Controller</a><ul class="toc-sidebar-sub-headers"></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#测试一下" class="toc-sidebar-link">测试一下：</a><ul class="toc-sidebar-sub-headers"></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#一、-requestparam注解对应的axios传参方法" class="toc-sidebar-link">一、@RequestParam注解对应的axios传参方法</a><ul class="toc-sidebar-sub-headers"><li class="toc-sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#params传参-推荐" class="toc-sidebar-link">params传参（推荐）</a></li><li class="toc-sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#formdata传参" class="toc-sidebar-link">FormData传参</a></li><li class="toc-sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#qs-stringfy传参" class="toc-sidebar-link">qs.stringfy传参</a></li></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#二、-requestbody的axios传参方法" class="toc-sidebar-link">二、@RequestBody的axios传参方法</a><ul class="toc-sidebar-sub-headers"></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#常用注解-2" class="toc-sidebar-link">常用注解</a><ul class="toc-sidebar-sub-headers"></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#bug" class="toc-sidebar-link">bug</a><ul class="toc-sidebar-sub-headers"></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#junit测试框架" class="toc-sidebar-link">junit测试框架</a><ul class="toc-sidebar-sub-headers"></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#mockito测试框架" class="toc-sidebar-link">Mockito测试框架</a><ul class="toc-sidebar-sub-headers"></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#真实servlet容器环境下的测试" class="toc-sidebar-link">真实servlet容器环境下的测试</a><ul class="toc-sidebar-sub-headers"><li class="toc-sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#springboottest-注解" class="toc-sidebar-link">@SpringBootTest 注解</a></li><li class="toc-sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#extendwith-runwith注解" class="toc-sidebar-link">@ExtendWith(@RunWith注解)</a></li><li class="toc-sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#transactional" class="toc-sidebar-link">@Transactional</a></li></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#mock测试" class="toc-sidebar-link">Mock测试</a><ul class="toc-sidebar-sub-headers"><li class="toc-sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#什么是mock" class="toc-sidebar-link">什么是Mock?</a></li><li class="toc-sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#场景实践" class="toc-sidebar-link">场景实践</a></li><li class="toc-sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#mockbean" class="toc-sidebar-link">@MockBean</a></li></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#轻量级测试" class="toc-sidebar-link">轻量级测试</a><ul class="toc-sidebar-sub-headers"><li class="toc-sidebar-sub-header toc-sidebar-depth-4"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#使用-webmvctest替换-springboottest" class="toc-sidebar-link">使用@WebMvcTest替换@SpringBootTest</a></li></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#mockmvc更多的用法总结" class="toc-sidebar-link">MockMvc更多的用法总结</a><ul class="toc-sidebar-sub-headers"></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#为什么需要api接口文档" class="toc-sidebar-link">为什么需要API接口文档?</a><ul class="toc-sidebar-sub-headers"></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#整合swagger2生成文档" class="toc-sidebar-link">整合swagger2生成文档</a><ul class="toc-sidebar-sub-headers"><li class="toc-sidebar-sub-header"><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#bug-2" class="toc-sidebar-link">bug</a></li></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#书写swagger注解" class="toc-sidebar-link">书写swagger注解</a><ul class="toc-sidebar-sub-headers"></ul></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/#生产环境下如何禁用swagger2" class="toc-sidebar-link">生产环境下如何禁用swagger2</a><ul class="toc-sidebar-sub-headers"></ul></li></ul></div></div></div></div></div>  <main class="footer"> <div class="copy-right"><span class="name"> 编程导航   |     </span> <a target="_blank" rel="noreferrer">
          
        </a></div></main></main> <aside class="page-sidebar"> <div class="page-side-toolbar"></div>  </aside></div><div class="global-ui"><!----></div></div>
    <script src="/assets/js/app.12ab4756.js" defer></script><script src="/assets/js/2.fd06b53e.js" defer></script><script src="/assets/js/62.bbb30ef1.js" defer></script>
  </body>
</html>
